I want to avoid duplicate drop on mxgraph canvas.
let say I have dragged Pipe on canvas 2nd time it should not allow it be dragged on canvas.
Question: how to avoid duplicate drop on canvas
Here is my working code drag with duplicate allowed
Drag and Drop
var graph = {};
function initCanvas() {
//This function is called onload of body itself and it will make the mxgraph canvas
graph = new mxGraph(document.getElementById('graph-wrapper'));
graph.htmlLabels = true;
graph.cellsEditable = false;
// render as HTML node always. You probably won't want that in real world though
graph.convertValueToString = function(cell) {
return cell.value;
}
const createDropHandler = function (cells, allowSplit) {
return function (graph, evt, target, x, y) {
const select = graph.importCells(cells, x, y, target);
graph.setSelectionCells(select);
};
};
const createDragPreview = function (width, height) {
var elt = document.createElement('div');
elt.style.border = '1px dashed black';
elt.style.width = width + 'px';
elt.style.height = height + 'px';
return elt;
};
const createDragSource = function (elt, dropHandler, preview) {
return mxUtils.makeDraggable(elt, graph, dropHandler, preview, 0, 0, graph.autoscroll, true, true);
};
const createItem = (id) => {
const elt = document.getElementById(id);
const width = elt.clientWidth;
const height = elt.clientHeight;
const cell = new mxCell('', new mxGeometry(0, 0, width, height), 'fillColor=none;strokeColor=none');
cell.vertex = true;
graph.model.setValue(cell, elt);
const cells = [cell];
const bounds = new mxRectangle(0, 0, width, height);
createDragSource(elt, createDropHandler(cells, true, false, bounds), createDragPreview(width, height), cells, bounds);
};
createItem("shape_1");
createItem("shape_2");
createItem("shape_3");
}
#graph-wrapper {
background: #333;
width: 100%;
height: 528px;
}
<html>
<head>
<title>Toolbar example for mxGraph</title>
<script type="text/javascript">
mxBasePath = 'https://jgraph.github.io/mxgraph/javascript/src';
</script>
<script src="https://jgraph.github.io/mxgraph/javascript/src/js/mxClient.js"></script>
<script src="./app.js"></script>
</head>
<body onload="initCanvas()">
<h4>Drag same box 2 times on the canvas. see duplicate is allowed</h4>
<div>
<div id="shape_1"
style="width: 100px; height: 100px; border-radius: 50%; background: red; display: inline-flex; text-align: center; color: #fff; align-items: center; justify-content: center;">
Pipe
</div>
<div draggable="true" id="shape_2"
style="width: 100px; height: 100px; border-radius: 5%; background: orange; display: inline-flex; text-align: center; color: #fff; align-items: center; justify-content: center;">
Team
</div>
<div draggable="true" id="shape_3"
style="width: 100px; height: 64px; background: #009688; display: inline-flex; text-align: center; color: #fff; align-items: center; justify-content: center; border-radius: 207px; flex-direction: column;">
<div> <svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000">
<path d="M0 0h24v24H0V0z" fill="none" />
<path
d="M11 7h2v2h-2zm0 4h2v6h-2zm1-9C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8z" />
</svg></div>
<div>Info</div>
</div>
</div>
<div id="graph-wrapper">
</div>
</body>
</html>
You just need check all current cells value of maxgarph in createDropHandler. So get all cells by graph.getModel().cells and then check to see if the cells you want to add exists or not:
let allcells = graph.getModel().cells;
for (var i in allcells)
if (allcells[i].value && (allcells[i].value.id == cells[0].value.id))
return;
var graph = {};
function initCanvas() {
//This function is called onload of body itself and it will make the mxgraph canvas
graph = new mxGraph(document.getElementById('graph-wrapper'));
graph.htmlLabels = true;
graph.cellsEditable = false;
// render as HTML node always. You probably won't want that in real world though
graph.convertValueToString = function (cell) {
return cell.value;
}
const createDropHandler = function (cells, allowSplit) {
return function (graph, evt, target, x, y) {
debugger
let allcells = graph.getModel().cells;
for (var i in allcells)
if (allcells[i].value && (allcells[i].value.id == cells[0].value.id))
return;
const select = graph.importCells(cells, x, y, target);
graph.setSelectionCells(select);
};
};
const createDragPreview = function (width, height) {
var elt = document.createElement('div');
elt.style.border = '1px dashed black';
elt.style.width = width + 'px';
elt.style.height = height + 'px';
return elt;
};
const createDragSource = function (elt, dropHandler, preview) {
return mxUtils.makeDraggable(elt, graph, dropHandler, preview, 0, 0, graph.autoscroll, true, true);
};
const createItem = (id) => {
const elt = document.getElementById(id);
const width = elt.clientWidth;
const height = elt.clientHeight;
const cell = new mxCell('', new mxGeometry(0, 0, width, height), 'fillColor=none;strokeColor=none');
cell.vertex = true;
graph.model.setValue(cell, elt);
const cells = [cell];
const bounds = new mxRectangle(0, 0, width, height);
createDragSource(elt, createDropHandler(cells, true, false, bounds), createDragPreview(width, height), cells, bounds);
};
createItem("shape_1");
createItem("shape_2");
createItem("shape_3");
}
<html>
<head>
<title>Toolbar example for mxGraph</title>
<script type="text/javascript">
mxBasePath = 'https://jgraph.github.io/mxgraph/javascript/src';
</script>
<script src="https://jgraph.github.io/mxgraph/javascript/src/js/mxClient.js"></script>
<script src="./app.js"></script>
<style>
#graph-wrapper {
background: #333;
width: 100%;
height: 528px;
}
</style>
</head>
<body onload="initCanvas()">
<h4>Drag same box 2 times on the canvas. see duplicate is allowed</h4>
<div>
<div id="shape_1"
style="width: 100px; height: 100px; border-radius: 50%; background: red; display: inline-flex; text-align: center; color: #fff; align-items: center; justify-content: center;">
Pipe
</div>
<div draggable="true" id="shape_2"
style="width: 100px; height: 100px; border-radius: 5%; background: orange; display: inline-flex; text-align: center; color: #fff; align-items: center; justify-content: center;">
Team
</div>
<div draggable="true" id="shape_3"
style="width: 100px; height: 64px; background: #009688; display: inline-flex; text-align: center; color: #fff; align-items: center; justify-content: center; border-radius: 207px; flex-direction: column;">
<div>
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000">
<path d="M0 0h24v24H0V0z" fill="none" />
<path d="M11 7h2v2h-2zm0 4h2v6h-2zm1-9C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8z" />
</svg>
</div>
<div>Info</div>
</div>
</div>
<div id="graph-wrapper">
</div>
First of all, I don't have any idea about mxgraph. To solve your problem I made some research on documentation of the library and I didn't find any tips.
I used the basics of the Javascript to achieve this and I didn't touch any of your main code and flow. I just add the draggable_status to graph element on createDropHandler function.
When you drag once, the below code add a status to the object and you can't drag anymore just adding a simple control.
graph.draggable_status = false;
To control, should be in the same function - const createDropHandler = function (cells, allowSplit)
if(graph.draggable_status != false){
const select = graph.importCells(cells, x, y, target);
graph.setSelectionCells(select);
graph.draggable_status = false;
}
The full code snippet is here;
var graph = {};
function initCanvas() {
//This function is called onload of body itself and it will make the mxgraph canvas
graph = new mxGraph(document.getElementById('graph-wrapper'));
graph.htmlLabels = true;
graph.cellsEditable = false;
// render as HTML node always. You probably won't want that in real world though
graph.convertValueToString = function(cell) {
return cell.value;
}
const createDropHandler = function (cells, allowSplit) {
return function (graph, evt, target, x, y) {
if(graph.draggable_status != false){
const select = graph.importCells(cells, x, y, target);
graph.setSelectionCells(select);
graph.draggable_status = false;
}
};
};
const createDragPreview = function (width, height) {
var elt = document.createElement('div');
elt.style.border = '1px dashed black';
elt.style.width = width + 'px';
elt.style.height = height + 'px';
return elt;
};
const createDragSource = function (elt, dropHandler, preview) {
return mxUtils.makeDraggable(elt, graph, dropHandler, preview, 0, 0, graph.autoscroll, true, true);
};
const createItem = (id) => {
const elt = document.getElementById(id);
const width = elt.clientWidth;
const height = elt.clientHeight;
const cell = new mxCell('', new mxGeometry(0, 0, width, height), 'fillColor=none;strokeColor=none');
cell.vertex = true;
graph.model.setValue(cell, elt);
const cells = [cell];
const bounds = new mxRectangle(0, 0, width, height);
createDragSource(elt, createDropHandler(cells, true, false, bounds), createDragPreview(width, height), cells, bounds);
};
createItem("shape_1");
createItem("shape_2");
createItem("shape_3");
}
#graph-wrapper {
background: #333;
width: 100%;
height: 528px;
}
<html>
<head>
<title>Toolbar example for mxGraph</title>
<script type="text/javascript">
mxBasePath = 'https://jgraph.github.io/mxgraph/javascript/src';
</script>
<script src="https://jgraph.github.io/mxgraph/javascript/src/js/mxClient.js"></script>
<script src="./app.js"></script>
</head>
<body onload="initCanvas()">
<h4>Drag same box 2 times on the canvas. see duplicate is allowed</h4>
<div>
<div id="shape_1"
style="width: 100px; height: 100px; border-radius: 50%; background: red; display: inline-flex; text-align: center; color: #fff; align-items: center; justify-content: center;">
Pipe
</div>
<div draggable="true" id="shape_2"
style="width: 100px; height: 100px; border-radius: 5%; background: orange; display: inline-flex; text-align: center; color: #fff; align-items: center; justify-content: center;">
Team
</div>
<div draggable="true" id="shape_3"
style="width: 100px; height: 64px; background: #009688; display: inline-flex; text-align: center; color: #fff; align-items: center; justify-content: center; border-radius: 207px; flex-direction: column;">
<div> <svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000">
<path d="M0 0h24v24H0V0z" fill="none" />
<path
d="M11 7h2v2h-2zm0 4h2v6h-2zm1-9C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8z" />
</svg></div>
<div>Info</div>
</div>
</div>
<div id="graph-wrapper">
</div>
</body>
</html>
Related
I am using glidejs for some slideshows on a webpage and I love it. Now I want to slide some content. The first element should be aligned left, the last aligned right and all other elements aligned centered.
I got some good results with focusAt for the first element, but it doenst seem to work for the last element.
var glide = new Glide('#glide', {
type: 'slider',
focusAt: 'center',
perView: 1.5,
focusAt: 0
})
glide.on("run", () => {
var focusAt = "center";
if (glide.index === 0) focusAt = 0;
glide.update({
focusAt: focusAt
});
});
glide.mount();
#glide {
text-align: center;
}
.glide__track {
border: 2px solid #000;
width: 300px;
margin: 10px auto;
}
.glide__slide {
height: 150px;
text-align: center;
line-height: 150px;
border: 2px solid #000;
background-color: #ccc;
font-size: 64px;
font-weight: bold;
font-family: arial;
color: #999;
}
.glide__bullet {
width: 10px;
height: 10px;
display: inline-block;
padding: 0;
}
.glide__bullet--active {
background-color: #000;
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/Glide.js/3.2.0/css/glide.core.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Glide.js/3.5.0/glide.min.js"></script>
<div id="glide" class="glide glide--ltr glide--slider glide--swipeable">
<div class="glide__track" data-glide-el="track">
<ul class="glide__slides">
<li class="glide__slide">0</li>
<li class="glide__slide">1</li>
<li class="glide__slide">2</li>
<li class="glide__slide">3</li>
<li class="glide__slide">4</li>
</ul>
</div>
<div data-glide-el="controls">
<button class="slider__arrow slider__arrow--prev glide__arrow glide__arrow--prev" data-glide-dir="<">
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24">
<path d="M0 12l10.975 11 2.848-2.828-6.176-6.176H24v-3.992H7.646l6.176-6.176L10.975 1 0 12z"></path>
</svg>
</button>
<button class="slider__arrow slider__arrow--next glide__arrow glide__arrow--next" data-glide-dir=">">
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24">
<path d="M13.025 1l-2.847 2.828 6.176 6.176h-16.354v3.992h16.354l-6.176 6.176 2.847 2.828 10.975-11z"></path>
</svg>
</button>
</div>
<div class="slider__bullets glide__bullets" data-glide-el="controls[nav]">
<button class="glide__bullet" data-glide-dir="=0"></button>
<button class="glide__bullet" data-glide-dir="=1"></button>
<button class="glide__bullet" data-glide-dir="=2"></button>
<button class="glide__bullet" data-glide-dir="=3"></button>
<button class="glide__bullet" data-glide-dir="=4"></button>
</div>
</div>
Any ideas?
Solved the problem with a mutator.
const glide = new Glide(container, {
type: "slider",
perView: 1.25,
focusAt: 0,
gap: 20,
});
const mutator = function (Glide, Components, Events) {
return {
modify(translate) {
// First slide
if (Glide.index === 0) {
// Move slide 20 pixels from left
return translate - 20;
// Last slide
} else if (Glide.index === Components.Sizes.length - 1) {
// Move slide 20 pixels from right
return translate - (Components.Sizes.width - Components.Sizes.slideWidth) + 20;
// Other slides
} else {
// Center slide
return translate - (Components.Sizes.width - Components.Sizes.slideWidth) / 2;
}
},
};
};
glide.mutate([mutator]).mount();
This question already has an answer here:
Cannot scale svg if created with js
(1 answer)
Closed 10 months ago.
I'm trying to add an svg dynamically to a menu, but I'm having a problem setting the viewbox.
When I inline the svgs (svg and svg-2), the viewbox has to be correct for the respective icon (0 0 512 512 and 0 0 24 24), as expected. Changing the viewbox removes the icon from view.
But for my dynamically added icons (dynamic-svg and dynamic-svg-2), changing the viewbox values (0 0 24 24 and 0 0 512 512) does nothing. In fact I can't get dynamic-svg-2 to show at all. dynamic-svg continues to display even if I change the viewbox to random values.
I must be doing something wrong, or have a bug somewhere, but I really can't see it. Would appreciate if someone could take a look. Thanks
codepen
const container = document.querySelector('.container');
const svgWrapper = document.createElement('div');
svgWrapper.className = 'dynamic-svg-wrapper'
const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg")
const path1 = document.createElementNS("http://www.w3.org/2000/svg", 'path')
svg.setAttribute("aria-hidden","true");
svg.setAttribute('viewbox', '0 0 24 24');
svg.setAttribute('class', 'dynamic-svg');
path1.setAttribute('d', `M9 8h-3v4h3v12h5v-12h3.642l.358-4h-4v-1.667c0-.955.192-1.333 1.115-1.333h2.885v-5h-3.808c-3.596 0-5.192 1.583-5.192 4.615v3.385z`);
path1.setAttribute('fill', '#000000');
svg.append(path1);
svgWrapper.append(svg);
container.append(svgWrapper);
const svgWrapper2 = document.createElement('div');
svgWrapper2.className = 'dynamic-svg-wrapper-2'
const svg2 = document.createElementNS("http://www.w3.org/2000/svg", "svg")
const path2 = document.createElementNS("http://www.w3.org/2000/svg", 'path')
svg2.setAttribute("aria-hidden","true");
svg2.setAttribute('viewbox', '0 0 512 512');
svg2.setAttribute('class', 'dynamic-svg-2');
path2.setAttribute('d', `M461.6,109.6l-54.9-43.3c-1.7-1.4-3.8-2.4-6.2-2.4c-2.4,0-4.6,1-6.3,2.5L194.5,323c0,0-78.5-75.5-80.7-77.7
c-2.2-2.2-5.1-5.9-9.5-5.9c-4.4,0-6.4,3.1-8.7,5.4c-1.7,1.8-29.7,31.2-43.5,45.8c-0.8,0.9-1.3,1.4-2,2.1c-1.2,1.7-2,3.6-2,5.7
c0,2.2,0.8,4,2,5.7l2.8,2.6c0,0,139.3,133.8,141.6,136.1c2.3,2.3,5.1,5.2,9.2,5.2c4,0,7.3-4.3,9.2-6.2L462,121.8
c1.2-1.7,2-3.6,2-5.8C464,113.5,463,111.4,461.6,109.6z`);
path2.setAttribute('fill', '#000000');
svg2.append(path2);
svgWrapper2.append(svg2);
container.append(svgWrapper2);
* {
margin: 0;
}
.container {
display: flex;
justify-content: center;
width: 100%;
height: 100vh;
background-color: lightgrey;
}
.svg-wrapper {
display: flex;
width: 24px;
height: 24px;
border: 1px solid black;
margin: 10px;
}
.svg {
display: flex;
width: 24px;
height: 24px;
}
.svg-wrapper-2 {
display: flex;
width: 24px;
height: 24px;
border: 1px solid black;
margin: 10px;
}
.dynamic-svg-wrapper {
display: flex;
width: 24px;
height: 24px;
border: 1px solid black;
margin: 10px;
}
.dynamic-svg {
display: flex;
width: 24px;
height: 24px;
}
.dynamic-svg-wrapper-2 {
display: flex;
width: 24px;
height: 24px;
border: 1px solid black;
margin: 10px;
}
.dynamic-svg-2 {
display: flex;
width: 24px;
height: 24px;
}
<div class="container">
<div class="svg-wrapper">
<svg class="svg" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 512 512">
<path d="M461.6,109.6l-54.9-43.3c-1.7-1.4-3.8-2.4-6.2-2.4c-2.4,0-4.6,1-6.3,2.5L194.5,323c0,0-78.5-75.5-80.7-77.7
c-2.2-2.2-5.1-5.9-9.5-5.9c-4.4,0-6.4,3.1-8.7,5.4c-1.7,1.8-29.7,31.2-43.5,45.8c-0.8,0.9-1.3,1.4-2,2.1c-1.2,1.7-2,3.6-2,5.7
c0,2.2,0.8,4,2,5.7l2.8,2.6c0,0,139.3,133.8,141.6,136.1c2.3,2.3,5.1,5.2,9.2,5.2c4,0,7.3-4.3,9.2-6.2L462,121.8
c1.2-1.7,2-3.6,2-5.8C464,113.5,463,111.4,461.6,109.6z"/>
</svg>
</div>
<div class="svg-wrapper-2">
<svg class='svg-2' xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9 8h-3v4h3v12h5v-12h3.642l.358-4h-4v-1.667c0-.955.192-1.333 1.115-1.333h2.885v-5h-3.808c-3.596 0-5.192 1.583-5.192 4.615v3.385z"/></svg>
</div>
</div>
]1
You can save yourself a lot of headaches using modern technologies,
A <svg-icon> Web Component, supported in all modern browsers.
I processed some of your <path> with https://yqnn.github.io/svg-path-editor/, to make them smaller.
You don't need 0.nnn precision when you stuff a 512x512 viewBox in 40x40 pixels
It does not matter where or when you execute customElements.define;
all existing or new <svg-icon> will automagically upgrade.
<style>
div {
display: flex; justify-content: center;
width:100%; background:pink;
}
</style>
<div>
<svg-icon>
<path fill="green" d="m461.6 109.6-54.9-43.3c-1.7-1.4-3.8-2.4-6.2-2.4-2.4 0-4.6 1-6.3 2.5l-199.7 256.6c0 0-78.5-75.5-80.7-77.7-2.2-2.2-5.1-5.9-9.5-5.9-4.4 0-6.4 3.1-8.7 5.4-1.7 1.8-29.7 31.2-43.5 45.8-.8.9-1.3 1.4-2 2.1-1.2 1.7-2 3.6-2 5.7 0 2.2.8 4 2 5.7l2.8 2.6c0 0 139.3 133.8 141.6 136.1 2.3 2.3 5.1 5.2 9.2 5.2 4 0 7.3-4.3 9.2-6.2l249.1-320c1.2-1.7 2-3.6 2-5.8 0-2.5-1-4.6-2.4-6.4z" />
</svg-icon>
<svg-icon vb="5120">
<path fill="red" d="m4616 1096-549-433c-17-14-38-24-62-24-24 0-46 10-63 25l-1997 2566c0 0-785-755-807-777-22-22-51-59-95-59-44 0-64 31-87 54-17 18-297 312-435 458-8 9-13 14-20 21-12 17-20 36-20 57 0 22 8 40 20 57l28 26c0 0 1393 1338 1416 1361 23 23 51 52 92 52 40 0 73-43 92-62l2491-3200c12-17 20-36 20-58 0-25-10-46-24-64z" />
</svg-icon>
<svg-icon vb="24">
<path fill="blue" d="M9 8h-3v4h3v12h5v-12h3.642l.358-4h-4v-1.667c0-.955.192-1.333 1.115-1.333h2.885v-5h-3.808c-3.596 0-5.192 1.583-5.192 4.615v3.385z" />
</svg-icon>
<svg-icon vb="24">
<path fill="rebeccapurple" d="M9 8h-3v4h3v12h5v-12h3.642l.358-4h-4v-1.667c0-.955.192-1.333 1.115-1.333h2.885v-5h-3.808c-3.596 0-5.192 1.583-5.192 4.615v3.385z" />
</svg-icon>
</div>
<script>
customElements.define("svg-icon", class extends HTMLElement {
connectedCallback() {
let vb = this.getAttribute("vb") || 512;
setTimeout(() => { // wait till path innerHTML is parsed
let svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
svg.setAttribute("viewBox",`0 0 ${vb} ${vb}`);
svg.innerHTML = this.innerHTML;
this.replaceWith(svg); // Web Component did its job, no need to keep it
});
}
});
</script>
Take this idea some steps further and you get https://iconmeister.github.io
for (el of chart.children) {
i++
previous__element = chart.children[i - 1]
if (el.classList.contains('rule')) {
//pass
} else {
line = el.children[0].children[0]
pos1 = previous__element.children[2].getBoundingClientRect()
position1 = {
top: pos1.top,
left: pos1.left,
}
pos2 = el.children[2].getBoundingClientRect()
console.log(previous__element.children[2])
console.log(el.children[2])
position2 = {
top: pos2.top,
left: pos2.left,
}
line.setAttribute('x1', Math.trunc(pos1.left))
line.setAttribute('y1', Math.trunc(pos1.top))
line.setAttribute('x2', Math.trunc(pos1.left))
line.setAttribute('y2', Math.trunc(pos1.top))
line.setAttribute('stroke', 'white')
}
}
html that gets output:
For some reason this does not actually show the lines, when hovering over them in dev tools it shows the height and width is 0. I'm trying to get the line to connect to the markers in the elements.
In this example I use position relative/absolute on all the elements. I don't know if that fits your solution, but the core of the example is that the SVG document is in the background of all the boxes. So, all the lines could be placed in that one SVG document.
Maybe the reason why your lines are not showing up is that they mis the stroke-width or that your SVG element does not have a width and a height.
let chart = document.querySelector('#chart');
let poschart = chart.getBoundingClientRect();
let line = chart.querySelector('svg line');
let boxs = chart.querySelectorAll('div.box');
let pos1 = boxs[0].getBoundingClientRect();
line.setAttribute('x1', pos1.x+pos1.width/2-poschart.x);
line.setAttribute('y1', pos1.y+pos1.height/2-poschart.y);
let pos2 = boxs[1].getBoundingClientRect();
line.setAttribute('x2', pos2.x+pos2.width/2-poschart.x);
line.setAttribute('y2', pos2.y+pos2.height/2-poschart.y);
#chart {
position: relative;
width: 400px;
height: 300px;
margin: 10px 20px;
border: thin solid black;
}
#chart svg {
position: absolute;
}
.box {
position: absolute;
padding: .5em;
border: thin solid black;
background-color: white;
}
<div id="chart">
<svg viewBox="0 0 400 300" width="100%" height="100%">
<line stroke="black" stroke-width="2"/>
</svg>
<div class="box" style="left:50px;top:180px">Box 1</div>
<div class="box" style="left:310px;top:100px">Box 2</div>
</div>
I'm attempting to create a game where you have to guess a randomly generated RGB on the screen with it's randomly generated visual counterpart on the screen out of 3 possible selections. For some reason, even though both the RGB value on the screen and the matching color both are assigned to the same variable, 2/10 times I get back a different RGB value for the color, not matching the typed out RGB value. The problem occurs in the 'DOMContentLoaded' event listener.
const easyBtn = document.querySelector('.easy'),
hardBtn = document.querySelector('.hard'),
rgb = document.querySelector('.rgb'),
rand1 = Math.round(Math.random()*300),
rand2 = Math.round(Math.random()*300),
rand3 = Math.round(Math.random()*300),
rand = `rgb(${rand1}, ${rand2}, ${rand3})`,
color1 = document.querySelector('.color1'),
color2 = document.querySelector('.color2'),
color3 = document.querySelector('.color3'),
colorsDiv = document.querySelector('.colors'),
newGame = document.querySelector('.new'),
rand4 = Math.round(Math.random()*301),
rand5 = Math.round(Math.random()*301),
rand6 = Math.round(Math.random()*301),
randd = `rgb(${rand4}, ${rand5}, ${rand6})`,
rand7 = Math.round(Math.random()*302),
rand8 = Math.round(Math.random()*302),
rand9 = Math.round(Math.random()*302),
randdd = `rgb(${rand7}, ${rand8}, ${rand9})`,
// Easy Random Colors
easyRand1 = Math.round(Math.random()*100),
easyRand2 = Math.round(Math.random()*100),
easyRand3 = Math.round(Math.random()*100),
easyRand = `rgb(${easyRand1}, ${easyRand2}, ${easyRand3})`;
easyRand4 = Math.round(Math.random()*101),
easyRand5 = Math.round(Math.random()*101),
easyRand6 = Math.round(Math.random()*101),
easyRandd = `rgb(${easyRand4}, ${easyRand5}, ${easyRand6})`;
easyRand7 = Math.round(Math.random()*102),
easyRand8 = Math.round(Math.random()*102),
easyRand9 = Math.round(Math.random()*103),
easyRanddd = `rgb(${easyRand7}, ${easyRand8}, ${easyRand9})`,
// Hard Random Colors
hardRand1 = Math.round(Math.random()*100),
hardRand2 = Math.round(Math.random()*130),
hardRand3 = Math.round(Math.random()*190),
hardRand = `rgb(${hardRand1}, ${hardRand2}, ${hardRand3})`;
hardRand4 = Math.round(Math.random()*87),
hardRand5 = Math.round(Math.random()*77),
hardRand6 = Math.round(Math.random()*158),
hardRandd = `rgb(${hardRand4}, ${hardRand5}, ${hardRand6})`;
hardRand7 = Math.round(Math.random()*89),
hardRand8 = Math.round(Math.random()*199),
hardRand9 = Math.round(Math.random()*121),
hardRanddd = `rgb(${hardRand7}, ${hardRand8}, ${hardRand9})`,
// Colors styling
colors = document.querySelector('.colors'),
allColors = document.querySelectorAll('.color');
let guessesLeft = 3
easyBtn.addEventListener('click', function() {
easyBtn.style.background = 'rgb(233, 150, 333)';
hardBtn.style.background = 'rgb(233, 230, 333)';
addNewColors();
})
hardBtn.addEventListener('click', function() {
hardBtn.style.background = 'rgb(233, 150, 333)';
easyBtn.style.background = 'rgb(233, 230, 333)';
addHardColors();
})
document.addEventListener('DOMContentLoaded', function() {
rgb.innerHTML = `<h1>${randd}</h1>`;
color1.style.backgroundColor = `${rand}`;
color2.style.backgroundColor = `${randd}`
color3.style.backgroundColor = `${randdd}`
for (var i = colorsDiv.children.length; i >= 0; i--) {
colorsDiv.appendChild(colorsDiv.children[[Math.random() * i | 0]]);
}
})
newGame.addEventListener('click', function() {
window.location.reload();
})
function addHardColors() {
rgb.innerHTML = `<h1>${easyRand}</h1>`;
color1.innerHTML = `<h1 class="color color1" style="background-color: ${easyRand};">`;
color2.innerHTML = `
<h1 class="color color2" style="background-color: ${easyRandd};">`;
color3.innerHTML = `<h1 class="color color3" style="background-color: ${easyRanddd};">`;
}
function addNewColors() {
rgb.innerHTML = `<h1>${hardRandd}</h1>`;
color1.innerHTML = `<h1 class="color color1" style="background-color: ${hardRand};">`;
color2.innerHTML = `
<h1 class="color color2" style="background-color: ${hardRandd};">`;
color3.innerHTML = `<h1 class="color color3" style="background-color: ${hardRanddd};">`;
}
allColors.forEach(function(color) {
color.addEventListener('click', function(e) {
let guess = e.target.style.backgroundColor;
let winningColor = document.querySelector('.rgb').textContent;
console.log(guess)
})
})
* {
box-sizing: border-box;
padding: 0;
margin: 0;
}
body {
font-family: 'Varela Round', sans-serif;
}
.header {
background: rgb(233, 150, 333);
height: 30vh;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
line-height: 7rem;
font-size: 40px;
}
.title {
color: white;
}
.buttons {
background:rgb(233, 230, 333);
height: 5vh;
display: flex;
justify-content: space-between;
}
.difficulty {
display: flex;
position: relative;
right: 190px;
}
.btn {
width: 100px;
background:rgb(233, 230, 333);
border: none;
font-family: 'Varela Round', sans-serif;
}
.colors {
display: flex;
justify-content: space-around;
align-items: center;
background: rgb(233, 230, 200);
height: 65vh;
}
.color {
height: 250px;
border-radius: 15px;
width: 400px;
}
.rgb {
font-size: 20px;
color: white;
}
.new {
position: relative;
left: 30px;
}
#media (max-width: 761px) {
.color {
width: 230px;
}
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="rgb.css">
<link href="https://fonts.googleapis.com/css2?family=Varela+Round&display=swap" rel="stylesheet">
<title>Document</title>
</head>
<body>
<header>
<div class="header">
<h1 class="title">RGB Guesser</h1>
<h1 class="rgb"></h1>
</div>
</header>
<section class="buttons">
<button class="btn new">NEW GAME</button>
<div class="difficulty">
<button class="easy btn">EASY</button>
<button class="hard btn">HARD</button>
</div>
</section>
<div class="colors-container">
<section class="colors">
<h1 class="color color1" style="background-color: red;"></h1>
<h1 class="color color2" style="background-color: purple"></h1>
<h1 class="color color3" style="background-color: blue;"></h1>
</section>
</div>
<script src="rgb.js"></script>
</body>
</html>
The maximum value of R, G or B in an RGB colour is 255.
However, in lines such as
rand1 = Math.round(Math.random()*300),
rand2 = Math.round(Math.random()*300),
rand3 = Math.round(Math.random()*300),
rand = `rgb(${rand1}, ${rand2}, ${rand3})`,
you may end up with rand1, rand2 or rand3 having values larger than 255.
Your browser will limit R, G and B values to 255, so if any of rand1 to rand3 are larger than this, then the colour shown by the browser will differ from the colour set in your code.
The fix is straightforward: don't generate colours with the R, G or B values larger than 255.
I have simple animation created using create js and ffmpegserver.js.
ffmpegserver.js.
This is a simple node server and library that sends canvas frames to the server and uses FFmpeg to compress the video. It can be used standalone or with CCapture.js.
Here is repo: video rendering demo.
on folder public, I have demos eg test3.html and test3.js
Test3.html
<!DOCTYPE html>
<html>
<head>
<title>TweenJS: Simple Tween Demo</title>
<style>
canvas {
border: 1px solid #08bf31;
justify-content: center;
display: flex;
align-items: center;
margin: 0px auto;
margin-bottom: 40px;
}
a {
width: 150px;
height: 45px;
background: red;
text-align: center;
display: flex;
justify-content: center;
align-items: center;
border-radius: 300px;
color: white;
}
#container{
flex-direction: column;
justify-content: center;
display: flex;
align-items: center;
margin: 0px auto;
}
#progress{
margin: 30px;
}
#preview{
margin: 40px;
width: 150px;
height: 45px;
background: deepskyblue;
color: white;
border: none;
border-radius: 300px;
}
</style>
</head>
<body onload="init();">
<div>
<div id="container">
<h1>Simple Tween Demo</h1>
<canvas id="testCanvas" width="500" height="400"></canvas>
<div id="progress"></div>
</div>
</div>
<script src="http://localhost:8081/ffmpegserver/CCapture.js"></script>
<script src="http://localhost:8081/ffmpegserver/ffmpegserver.js"></script>
<script src="https://code.createjs.com/1.0.0/createjs.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/tween.js/17.2.0/Tween.js"></script>
<script src="test3.js"></script>
</body>
</html>
Test3.js
/* eslint-disable eol-last */
/* eslint-disable no-undef */
/* eslint-disable quotes */
var canvas, stage;
function init() {
var framesPerSecond = 60;
var numFrames = framesPerSecond * 5; // a 5 second 60fps video
var frameNum = 0;
var progressElem = document.getElementById("progress");
var progressNode = document.createTextNode("");
progressElem.appendChild(progressNode);
function onProgress(progress) {
progressNode.nodeValue = (progress * 100).toFixed(1) + "%";
}
function showVideoLink(url, size) {
size = size ? (" [size: " + (size / 1024 / 1024).toFixed(1) + "meg]") : " [unknown size]";
var a = document.createElement("a");
a.href = url;
var filename = url;
var slashNdx = filename.lastIndexOf("/");
if (slashNdx >= 0) {
filename = filename.substr(slashNdx + 1);
}
a.download = filename;
a.appendChild(document.createTextNode("Download"));
var container = document.getElementById("container").insertBefore(a, progressElem);
}
var capturer = new CCapture( {
format: 'ffmpegserver',
//workersPath: "3rdparty/",
//format: 'gif',
//verbose: true,
framerate: framesPerSecond,
onProgress: onProgress,
//extension: ".mp4",
//codec: "libx264",
} );
capturer.start();
canvas = document.getElementById("testCanvas");
stage = new createjs.Stage(canvas);
var ball = new createjs.Shape();
ball.graphics.setStrokeStyle(5, 'round', 'round');
// eslint-disable-next-line quotes
ball.graphics.beginStroke('#000000');
ball.graphics.beginFill("#FF0000").drawCircle(0, 0, 50);
ball.graphics.setStrokeStyle(1, 'round', 'round');
ball.graphics.beginStroke('#000000');
ball.graphics.moveTo(0, 0);
ball.graphics.lineTo(0, 50);
ball.graphics.endStroke();
ball.x = 200;
ball.y = -50;
createjs.Tween.get(ball, {loop: -1})
.to({x: ball.x, y: canvas.height - 55, rotation: -360}, 1500, createjs.Ease.bounceOut)
.wait(1000)
.to({x: canvas.width - 55, rotation: 360}, 2500, createjs.Ease.bounceOut)
.wait(1000)
.to({scaleX: 2, scaleY: 2}, 2500, createjs.Ease.quadOut)
.wait(1000)
stage.addChild(ball);
createjs.Ticker.addEventListener("tick", stage);
function render() {
requestAnimationFrame(render);
capturer.capture( canvas );
++frameNum;
if (frameNum < numFrames) {
progressNode.nodeValue = "rendered frame# " + frameNum + " of " + numFrames;
} else if (frameNum === numFrames) {
capturer.stop();
capturer.save(showVideoLink);
}
}
render();
}
Everything works fine, you can test it yourself if you want by cloning the repo.
Right now animation rendering happens in client side, I would like this animation rendering to happen in the backend side
What do I need to change to make this animation rendering in backend server side using Nodejs? any help or suggestions will be appreciated.
Since you do all your animations in the canvas, you can use node-canvas to do the same in Node.js. (You have to double check that create.js also work in Node.js, though. If not, find another library or write those routines yourself).
Spawn ffmpeg into it's own process accepting input through a pipe (ffmpeg -i - -f rawvideo -pix_fmt rgba etc. The pipe will probably be different according to which server environment you use). After each frame is drawn, extract the image array using canvas.getContext('2d').getImageData(0, 0, width, height).data and pipe the result to to ffmpeg.