Related
I am trying to build a simple editor using fabricjs.
I did the following:
<!DOCTYPE html>
<html lang="en">
<head>
<link href="https://cdn.jsdelivr.net/npm/bootstrap#5.2.0-beta1/dist/css/bootstrap.min.css" rel="stylesheet" />
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons#1.9.0/font/bootstrap-icons.css" />
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.1/css/all.min.css">
<style>
html,
body {
height: 100%;
width: 100%;
padding: 0;
margin: 0;
}
body {
touch-action: none;
background-image: linear-gradient(to bottom left, rgb(214, 240, 201) 10%, rgba(255, 231, 191, 1) 80%);
-webkit-user-select: none;
-moz-user-select: -moz-none;
-ms-user-select: none;
user-select: none;
}
.input-group {
padding: 4px;
}
.canvas-container {
margin: 0 auto;
width: 100%;
overflow: hidden;
background: url(./transparent.png);
background-size: 15px 15px;
box-shadow: rgba(60, 64, 67, 0.3) 0px 1px 2px 0px, rgba(60, 64, 67, 0.15) 0px 1px 3px 1px;
}
.actived {
background: #fff9a8;
}
.list-group {
line-height: 35px;
}
.svg-icon {
width: 1em;
height: 1em;
}
.svg-icon path,
.svg-icon polygon,
.svg-icon rect {
fill: #4691f6;
}
.svg-icon circle {
stroke: #4691f6;
stroke-width: 1;
}
.fl {
float: left;
}
.fr {
float: right;
}
.input-group-text {
background-color: #f4f4f4;
}
.list-group-item {
padding: 2px 10px;
;
cursor: pointer;
}
.list-group {
border-radius: 0;
text-align: left;
}
</style>
</head>
<body>
<div class="container-fluid h-100">
<div class="row h-100">
<div class="col-sm-3 border-end text-center h-100 overflow-scroll bg-light py-3">
<hr>
<div class="form-floating w-100 mb-3 tour5">
<h6 class="mb-3">Add Element</h6>
<button class="btn btn-outline-primary" onclick="addText();" data-toggle="tooltip" data-placement="top" title="" data-bs-original-title="Add Text" aria-label="Add Text"><i
class="fa fa-font"></i></button>
<button class="btn btn-outline-primary" data-toggle="tooltip" data-placement="top" title="" onclick=" canvas.add(new fabric.Circle({ name: genNextName(), originX:'center', originY:'center', radius: 30, fill: '#000', top: 100, left: 100 }));" data-bs-original-title="Add Circle"
aria-label="Add Circle"><i
class="fa fa-circle"></i></button>
<button class="btn btn-outline-primary" data-toggle="tooltip" data-placement="top" title="" onclick=" canvas.add(new fabric.Triangle({ name: genNextName(), originX:'center', originY:'center', height:100, width:100, fill: '#000', top: 100, left: 100 }));"
data-bs-original-title="Add Triangle">
<svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="triangle" role="img"
xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" class=""
style="height: 16px;top: 9px;">
<path fill="currentColor"
d="M329.6 24c-18.4-32-64.7-32-83.2 0L6.5 440c-18.4 31.9 4.6 72 41.6 72H528c36.9 0 60-40 41.6-72l-240-416z"
class=""></path>
</svg>
</button>
</div>
<hr>
</div>
<div class="col-sm-6 my-auto py-4 overflow-hidden">
<div class="canvas-container" style="width: 329px; height: 329px; position: relative; user-select: none;"><canvas id="c" class="lower-canvas" width="329" height="329" style="position: absolute; width: 329px; height: 329px; left: 0px; top: 0px; touch-action: none; user-select: none;"></canvas><canvas class="upper-canvas " width="329" height="329" style="position: absolute; width: 329px; height: 329px; left: 0px; top: 0px; touch-action: manipulation; user-select: none; cursor: default;"></canvas>
</div>
</div>
<div class="col-sm-3 border-start py-3 h-100 overflow-scroll bg-light text-center">
<form class="form-inline pt-4" id="f" onsubmit="return false;" style="display: none;">
<div class="form-floating w-100 mb-4" style="">
<textarea type="text" class="form-control form-control-sm" name="text" oninput="canvas.getActiveObject().set({'text':this.value}); canvas.renderAll();" onmouseover="this.style.height = (this.scrollHeight)+'px';" rows="10" autocomplete="off"></textarea>
<label for="floatingInput" class="lable-sm">Text</label>
</div>
<div class="input-group input-group-sm mb-3 w-100 tour13">
<span class="input-group-text" id="inputGroup-sizing-sm">MaxHeight to Fit Text</span>
<input type="number" class="form-control" name="maxHeight" onkeyup="hide_mh_box(); show_mh_box();" onblur="hide_mh_box();" onfocus="show_mh_box();" autocomplete="off">
</div>
</form>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap#5.2.0-beta1/dist/js/bootstrap.bundle.min.js"></script>
<script src="https://code.jquery.com/jquery-3.6.0.js"></script>
<script src="https://code.jquery.com/ui/1.13.1/jquery-ui.js"></script>
<script src="https://unpkg.com/fabric#5.2.1/dist/fabric.min.js"></script>
<script src="https://rawcdn.githack.com/lyzerk/fabric-history/8c223cbdc8305307b4a8f8710f97da54d9146ffa/src/index.js"></script>
<script src="https://rawgit.com/fabricjs/fabric.js/master/lib/centering_guidelines.js"></script>
<script src="https://rawgit.com/fabricjs/fabric.js/master/lib/aligning_guidelines.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/webfont/1.5.10/webfont.js"></script>
<script>
var canvas;
var apiUrl;
var startedLoadingFamilies = false;
var to;
var scale;
fabric.Text.prototype.set({
_getNonTransformedDimensions() {
return new fabric.Point(this.width, this.height).scalarAdd(this.padding);
},
_calculateCurrentDimensions() {
return fabric.util.transformPoint(this._getTransformedDimensions(), this.getViewportTransform(), true);
}
});
function fitCanvas(w, h) {
var scaleW = (document.querySelectorAll(".col-sm-6")[0].offsetWidth - 100) / w;
var scaleH = (document.querySelectorAll("body")[0].offsetHeight - 100) / h;
scale = Math.min(scaleH, scaleW);
if (scale > 1) {
scale = 1;
}
canvas.setZoom(scale);
canvas.setWidth(w * scale);
canvas.setHeight(h * scale);
initAligningGuidelines(canvas);
initCenteringGuidelines(canvas);
canvas.renderAll();
console.log("finished!!");
}
(function() {
canvas = new fabric.Canvas('c', {
allowTouchScrolling: true
});
WebFont.load({
google: {
families: ["Inter:200"]
},
active: function() {
canvas.loadFromJSON({
"height": "500",
"width": "500",
"edit": "yes",
"objects": [],
"backgroundImage": {
"crossOrigin": "anonymous"
}
}, function() {
fitCanvas(500, 500);
})
}
});
canvas.on('before:render', function(opt) {
canvas.getObjects().map(function(o, i) {
if (o.type.toLowerCase() == "textbox") {
var maxH = (o.maxHeight) ? (o.maxHeight) : (canvas.height);
o.set({
'maxHeight': maxH
});
if (o.fontSize > 0 && (maxH > o.fontSize)) {
while (o.height > maxH) {
o.set({
'fontSize': o.fontSize - 1
});
}
}
}
if (o.circleFrame) {
o.set({
clipPath: new fabric.Circle({
radius: o.width / 2,
originX: 'center',
originY: 'center'
})
});
}
if ((o.rx || o.ry) && o.type == "image") {
let rx = (o.rx || 0);
let ry = (o.ry || 0);
o.set({
clipPath: new fabric.Rect({
rx: rx,
ry: ry,
height: o.height,
width: o.width,
originX: 'center',
originY: 'center'
})
});
}
});
});
})();
</script>
<script>
fabric.Object.prototype.objectCaching = false;
function changeDim() {
fitCanvas($("#widthC").val(), $("#heightC").val());
}
function setCanvasBI(src, o = "1") {
if (!src) {
src = $("#canvasBI").val();
}
if (src.length == 0) {
src = new fabric.Image('');
}
canvas.setBackgroundImage(src, canvas.renderAll.bind(canvas), {
opacity: o
});
}
(function() {
$(document).on("mouseenter", "[data-label]", function() {
$("#font-search").val($(this).data("label"));
loadFont($(this).data("label"));
});
fabric.Canvas.prototype.getItemByAttr = function(attr, name) {
var object = null,
objects = this.getObjects();
for (var i = 0, len = this.size(); i < len; i++) {
if (objects[i][attr] && objects[i][attr] == name) {
object = objects[i];
break;
}
}
return object;
};
$("form#f").hide();
var activeObject;
$("#f input, #f select").on("input", function() {
if (["height", "width", "top", "left", "strokeWidth", "charSpacing"].includes(this.name)) {
canvas.getActiveObject().set(this.name, parseFloat(this.value)).setCoords();
} else {
canvas.getActiveObject().set(this.name, this.value).setCoords();
}
canvas.renderAll();
});
canvas.preserveObjectStacking = true;
fabric.Object.prototype.toObject = (function(toObject) {
return function() {
return fabric.util.object.extend(toObject.call(this), {
name: this.name,
text: this.text
});
};
})(fabric.Object.prototype.toObject);
canvas.on('object:scaling', function(e) {
if (e.target.toObject().type != "image" && e.target.toObject().type != "circle") {
e.target.set({
width: e.target.width * e.target.scaleX,
height: e.target.height * e.target.scaleY,
scaleX: 1,
scaleY: 1
})
}
});
canvas.on('object:modified', function(opt) {
document.body.style.cursor = 'progress';
});
canvas.on("after:render", function() {
canvas.includeDefaultValues = false;
canvas.toObject().objects.forEach(function(layer, id) {
if (typeof layer.name !== 'undefined') {
canvas.getItemByAttr(`name`, layer.name).set({
"name": String.fromCharCode(65 + id).toLowerCase()
})
var actived = '';
if (canvas.getActiveObject()) {
actived = (canvas.getActiveObject().name == layer.name) ? " actived" : "";
}
}
});
document.body.style.cursor = 'default'
});
canvas.on("selection:created", function(obj) {
if ("image" == obj.target.type) {
canvas.getActiveObject().setControlsVisibility({
mb: false,
ml: false,
mt: false,
mr: false
});
}
$("form#f input[type!='hidden'], #f select").parent().hide();
$("form#f").hide();
canvas.renderAll();
showForm();
});
canvas.on("selection:updated", function(obj) {
if ("image" == obj.target.type) {
canvas.getActiveObject().setControlsVisibility({
mb: false,
ml: false,
mt: false,
mr: false
});
}
canvas.renderAll();
$("form#f").hide();
$("form#f input[type!='hidden'] , #f select, #f textarea").parent().hide();
showForm();
});
canvas.on("selection:cleared", function() {
canvas.renderAll();
$("form#f").hide();
$("form#f input[type!='hidden'], #f select, #f textarea").parent().hide();
});
canvas.hoverCursor = 'default';
canvas.on('mouse:over', function(e) {
if (e.target) {
e.target._renderControls(canvas.contextTop, {
hasControls: false
})
}
});
canvas.on('mouse:out', function(e) {
canvas.clearContext(canvas.contextTop);
});
canvas.on('mouse:down', function(e) {
canvas.clearContext(canvas.contextTop);
});
function showForm() {
$("form#f").show();
activeObject = canvas.getActiveObject();
var v;
for (i in activeObject) {
v = activeObject[i];
if (typeof v != "undefined") {
$("textarea[name='" + i + "']").val(v).parent().show();
$("input[name='" + i + "']").val(v).parent().show();
$("select[name='" + i + "']").val(v).parent().show();
}
}
}
addText = function() {
var text = new fabric.Textbox("Edit this Text", {
name: genNextName(),
left: canvas.getWidth() / canvas.getZoom() / 2,
top: canvas.getHeight() / canvas.getZoom() / 2,
width: (canvas.getWidth() / canvas.getZoom()) / 2,
fill: "#000000",
originX: "center",
originY: "center",
fontFamily: "Inter",
fontWeight: 400,
fontSize: 60,
padding: 20
});
text.setControlsVisibility({
mt: false,
mb: false,
ml: true,
mr: true,
tl: true,
tr: true,
bl: true,
br: true
});
canvas.add(text);
canvas.setActiveObject(text);
canvas.renderAll();
};
})();
function hide_mh_box() {
canvas.remove(canvas.getItemByAttr("isBB", true));
}
function show_mh_box() {
var h = parseInt($("[name=maxHeight]").val());
var n = new fabric.Rect({
top: canvas.getActiveObject().get('top'),
left: canvas.getActiveObject().get('left'),
width: canvas.getActiveObject().get('width'),
height: h,
originX: canvas.getActiveObject().get('originX'),
originY: canvas.getActiveObject().get('originY'),
angle: canvas.getActiveObject().get('angle'),
opacity: 1,
strokeWidth: 2,
stroke: "#FF00FF",
fill: "rgba(0,0,0,0)",
evented: !1,
isBB: true
});
canvas.add(n);
canvas.renderAll();
}
var visited = [];
</script>
<script>
function genNextName() {
canvas.renderAll();
var total = canvas.getObjects().length;
return String.fromCharCode(65 + total).toLowerCase()
}
</script>
</body>
</html>
When I click on an element f.ex. the triangle it is added to the canvas. When I click on the added triangle a menu on the right should go up. However, the element does not get selected.
Any suggestions what I am doing wrong?
I appreciate your replies!
you have second canvas element next to your canvas, it prevents from clicking on first canvas element, removing this second canvas solves this issue
<canvas class="upper-canvas " width="329" height="329" style="position: absolute; width: 329px; height: 329px; left: 0px; top: 0px; touch-action: manipulation; user-select: none; cursor: default;"></canvas>
<!DOCTYPE html>
<html lang="en">
<head>
<link href="https://cdn.jsdelivr.net/npm/bootstrap#5.2.0-beta1/dist/css/bootstrap.min.css" rel="stylesheet" />
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons#1.9.0/font/bootstrap-icons.css" />
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.1/css/all.min.css">
<style>
html,
body {
height: 100%;
width: 100%;
padding: 0;
margin: 0;
}
body {
touch-action: none;
background-image: linear-gradient(to bottom left, rgb(214, 240, 201) 10%, rgba(255, 231, 191, 1) 80%);
-webkit-user-select: none;
-moz-user-select: -moz-none;
-ms-user-select: none;
user-select: none;
}
.input-group {
padding: 4px;
}
.canvas-container {
margin: 0 auto;
width: 100%;
overflow: hidden;
background: url(./transparent.png);
background-size: 15px 15px;
box-shadow: rgba(60, 64, 67, 0.3) 0px 1px 2px 0px, rgba(60, 64, 67, 0.15) 0px 1px 3px 1px;
}
.actived {
background: #fff9a8;
}
.list-group {
line-height: 35px;
}
.svg-icon {
width: 1em;
height: 1em;
}
.svg-icon path,
.svg-icon polygon,
.svg-icon rect {
fill: #4691f6;
}
.svg-icon circle {
stroke: #4691f6;
stroke-width: 1;
}
.fl {
float: left;
}
.fr {
float: right;
}
.input-group-text {
background-color: #f4f4f4;
}
.list-group-item {
padding: 2px 10px;
;
cursor: pointer;
}
.list-group {
border-radius: 0;
text-align: left;
}
</style>
</head>
<body>
<div class="container-fluid h-100">
<div class="row h-100">
<div class="col-sm-3 border-end text-center h-100 overflow-scroll bg-light py-3">
<hr>
<div class="form-floating w-100 mb-3 tour5">
<h6 class="mb-3">Add Element</h6>
<button class="btn btn-outline-primary" onclick="addText();" data-toggle="tooltip" data-placement="top" title="" data-bs-original-title="Add Text" aria-label="Add Text"><i
class="fa fa-font"></i></button>
<button class="btn btn-outline-primary" data-toggle="tooltip" data-placement="top" title="" onclick=" canvas.add(new fabric.Circle({ name: genNextName(), originX:'center', originY:'center', radius: 30, fill: '#000', top: 100, left: 100 }));" data-bs-original-title="Add Circle"
aria-label="Add Circle"><i
class="fa fa-circle"></i></button>
<button class="btn btn-outline-primary" data-toggle="tooltip" data-placement="top" title="" onclick=" canvas.add(new fabric.Triangle({ name: genNextName(), originX:'center', originY:'center', height:100, width:100, fill: '#000', top: 100, left: 100 }));"
data-bs-original-title="Add Triangle">
<svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="triangle" role="img"
xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" class=""
style="height: 16px;top: 9px;">
<path fill="currentColor"
d="M329.6 24c-18.4-32-64.7-32-83.2 0L6.5 440c-18.4 31.9 4.6 72 41.6 72H528c36.9 0 60-40 41.6-72l-240-416z"
class=""></path>
</svg>
</button>
</div>
<hr>
</div>
<div class="col-sm-6 my-auto py-4 overflow-hidden">
<div class="canvas-container" style="width: 329px; height: 329px; position: relative; user-select: none;"><canvas id="c" class="lower-canvas" width="329" height="329" style="position: absolute; width: 329px; height: 329px; left: 0px; top: 0px; touch-action: none; user-select: none;"></canvas>
</div>
</div>
<div class="col-sm-3 border-start py-3 h-100 overflow-scroll bg-light text-center">
<form class="form-inline pt-4" id="f" onsubmit="return false;" style="display: none;">
<div class="form-floating w-100 mb-4" style="">
<textarea type="text" class="form-control form-control-sm" name="text" oninput="canvas.getActiveObject().set({'text':this.value}); canvas.renderAll();" onmouseover="this.style.height = (this.scrollHeight)+'px';" rows="10" autocomplete="off"></textarea>
<label for="floatingInput" class="lable-sm">Text</label>
</div>
<div class="input-group input-group-sm mb-3 w-100 tour13">
<span class="input-group-text" id="inputGroup-sizing-sm">MaxHeight to Fit Text</span>
<input type="number" class="form-control" name="maxHeight" onkeyup="hide_mh_box(); show_mh_box();" onblur="hide_mh_box();" onfocus="show_mh_box();" autocomplete="off">
</div>
</form>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap#5.2.0-beta1/dist/js/bootstrap.bundle.min.js"></script>
<script src="https://code.jquery.com/jquery-3.6.0.js"></script>
<script src="https://code.jquery.com/ui/1.13.1/jquery-ui.js"></script>
<script src="https://unpkg.com/fabric#5.2.1/dist/fabric.min.js"></script>
<script src="https://rawcdn.githack.com/lyzerk/fabric-history/8c223cbdc8305307b4a8f8710f97da54d9146ffa/src/index.js"></script>
<script src="https://rawgit.com/fabricjs/fabric.js/master/lib/centering_guidelines.js"></script>
<script src="https://rawgit.com/fabricjs/fabric.js/master/lib/aligning_guidelines.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/webfont/1.5.10/webfont.js"></script>
<script>
var canvas;
var apiUrl;
var startedLoadingFamilies = false;
var to;
var scale;
fabric.Text.prototype.set({
_getNonTransformedDimensions() {
return new fabric.Point(this.width, this.height).scalarAdd(this.padding);
},
_calculateCurrentDimensions() {
return fabric.util.transformPoint(this._getTransformedDimensions(), this.getViewportTransform(), true);
}
});
function fitCanvas(w, h) {
var scaleW = (document.querySelectorAll(".col-sm-6")[0].offsetWidth - 100) / w;
var scaleH = (document.querySelectorAll("body")[0].offsetHeight - 100) / h;
scale = Math.min(scaleH, scaleW);
if (scale > 1) {
scale = 1;
}
canvas.setZoom(scale);
canvas.setWidth(w * scale);
canvas.setHeight(h * scale);
initAligningGuidelines(canvas);
initCenteringGuidelines(canvas);
canvas.renderAll();
console.log("finished!!");
}
(function() {
canvas = new fabric.Canvas('c', {
allowTouchScrolling: true
});
WebFont.load({
google: {
families: ["Inter:200"]
},
active: function() {
canvas.loadFromJSON({
"height": "500",
"width": "500",
"edit": "yes",
"objects": [],
"backgroundImage": {
"crossOrigin": "anonymous"
}
}, function() {
fitCanvas(500, 500);
})
}
});
canvas.on('before:render', function(opt) {
canvas.getObjects().map(function(o, i) {
if (o.type.toLowerCase() == "textbox") {
var maxH = (o.maxHeight) ? (o.maxHeight) : (canvas.height);
o.set({
'maxHeight': maxH
});
if (o.fontSize > 0 && (maxH > o.fontSize)) {
while (o.height > maxH) {
o.set({
'fontSize': o.fontSize - 1
});
}
}
}
if (o.circleFrame) {
o.set({
clipPath: new fabric.Circle({
radius: o.width / 2,
originX: 'center',
originY: 'center'
})
});
}
if ((o.rx || o.ry) && o.type == "image") {
let rx = (o.rx || 0);
let ry = (o.ry || 0);
o.set({
clipPath: new fabric.Rect({
rx: rx,
ry: ry,
height: o.height,
width: o.width,
originX: 'center',
originY: 'center'
})
});
}
});
});
})();
</script>
<script>
fabric.Object.prototype.objectCaching = false;
function changeDim() {
fitCanvas($("#widthC").val(), $("#heightC").val());
}
function setCanvasBI(src, o = "1") {
if (!src) {
src = $("#canvasBI").val();
}
if (src.length == 0) {
src = new fabric.Image('');
}
canvas.setBackgroundImage(src, canvas.renderAll.bind(canvas), {
opacity: o
});
}
(function() {
$(document).on("mouseenter", "[data-label]", function() {
$("#font-search").val($(this).data("label"));
loadFont($(this).data("label"));
});
fabric.Canvas.prototype.getItemByAttr = function(attr, name) {
var object = null,
objects = this.getObjects();
for (var i = 0, len = this.size(); i < len; i++) {
if (objects[i][attr] && objects[i][attr] == name) {
object = objects[i];
break;
}
}
return object;
};
$("form#f").hide();
var activeObject;
$("#f input, #f select").on("input", function() {
if (["height", "width", "top", "left", "strokeWidth", "charSpacing"].includes(this.name)) {
canvas.getActiveObject().set(this.name, parseFloat(this.value)).setCoords();
} else {
canvas.getActiveObject().set(this.name, this.value).setCoords();
}
canvas.renderAll();
});
canvas.preserveObjectStacking = true;
fabric.Object.prototype.toObject = (function(toObject) {
return function() {
return fabric.util.object.extend(toObject.call(this), {
name: this.name,
text: this.text
});
};
})(fabric.Object.prototype.toObject);
canvas.on('object:scaling', function(e) {
if (e.target.toObject().type != "image" && e.target.toObject().type != "circle") {
e.target.set({
width: e.target.width * e.target.scaleX,
height: e.target.height * e.target.scaleY,
scaleX: 1,
scaleY: 1
})
}
});
canvas.on('object:modified', function(opt) {
document.body.style.cursor = 'progress';
});
canvas.on("after:render", function() {
canvas.includeDefaultValues = false;
canvas.toObject().objects.forEach(function(layer, id) {
if (typeof layer.name !== 'undefined') {
canvas.getItemByAttr(`name`, layer.name).set({
"name": String.fromCharCode(65 + id).toLowerCase()
})
var actived = '';
if (canvas.getActiveObject()) {
actived = (canvas.getActiveObject().name == layer.name) ? " actived" : "";
}
}
});
document.body.style.cursor = 'default'
});
canvas.on("selection:created", function(obj) {
if ("image" == obj.target?.type) {
canvas.getActiveObject().setControlsVisibility({
mb: false,
ml: false,
mt: false,
mr: false
});
}
$("form#f input[type!='hidden'], #f select").parent().hide();
$("form#f").hide();
canvas.renderAll();
showForm();
});
canvas.on("selection:updated", function(obj) {
if ("image" == obj.target?.type) {
canvas.getActiveObject().setControlsVisibility({
mb: false,
ml: false,
mt: false,
mr: false
});
}
canvas.renderAll();
$("form#f").hide();
$("form#f input[type!='hidden'] , #f select, #f textarea").parent().hide();
showForm();
});
canvas.on("selection:cleared", function() {
canvas.renderAll();
$("form#f").hide();
$("form#f input[type!='hidden'], #f select, #f textarea").parent().hide();
});
canvas.hoverCursor = 'default';
canvas.on('mouse:over', function(e) {
if (e.target) {
e.target._renderControls(canvas.contextTop, {
hasControls: false
})
}
});
canvas.on('mouse:out', function(e) {
canvas.clearContext(canvas.contextTop);
});
canvas.on('mouse:down', function(e) {
canvas.clearContext(canvas.contextTop);
});
function showForm() {
$("form#f").show();
activeObject = canvas.getActiveObject();
var v;
for (i in activeObject) {
v = activeObject[i];
if (typeof v != "undefined") {
$("textarea[name='" + i + "']").val(v).parent().show();
$("input[name='" + i + "']").val(v).parent().show();
$("select[name='" + i + "']").val(v).parent().show();
}
}
}
addText = function() {
var text = new fabric.Textbox("Edit this Text", {
name: genNextName(),
left: canvas.getWidth() / canvas.getZoom() / 2,
top: canvas.getHeight() / canvas.getZoom() / 2,
width: (canvas.getWidth() / canvas.getZoom()) / 2,
fill: "#000000",
originX: "center",
originY: "center",
fontFamily: "Inter",
fontWeight: 400,
fontSize: 60,
padding: 20
});
text.setControlsVisibility({
mt: false,
mb: false,
ml: true,
mr: true,
tl: true,
tr: true,
bl: true,
br: true
});
canvas.add(text);
canvas.setActiveObject(text);
canvas.renderAll();
};
})();
function hide_mh_box() {
canvas.remove(canvas.getItemByAttr("isBB", true));
}
function show_mh_box() {
var h = parseInt($("[name=maxHeight]").val());
var n = new fabric.Rect({
top: canvas.getActiveObject().get('top'),
left: canvas.getActiveObject().get('left'),
width: canvas.getActiveObject().get('width'),
height: h,
originX: canvas.getActiveObject().get('originX'),
originY: canvas.getActiveObject().get('originY'),
angle: canvas.getActiveObject().get('angle'),
opacity: 1,
strokeWidth: 2,
stroke: "#FF00FF",
fill: "rgba(0,0,0,0)",
evented: !1,
isBB: true
});
canvas.add(n);
canvas.renderAll();
}
var visited = [];
</script>
<script>
function genNextName() {
canvas.renderAll();
var total = canvas.getObjects().length;
return String.fromCharCode(65 + total).toLowerCase()
}
</script>
</body>
</html>
I am using FabricJS 5 and have implemented a simple display for my layers.
When I press the pencil button I open a prompt and rename the layer using the following function:
function RenameLayer(name) {
let layerObj = canvas.getItemByAttr("name", name);
let newName = prompt("Rename Layer?", layerObj.name);
while (checkIfNameExists(newName)) {
alert("Your layer name: " + layerObj.name + " is NOT unique please rename your layer differently.")
newName = prompt("Rename Layer?", layerObj.name);
}
layerObj.name = newName;
canvas.renderAll();
}
function checkIfNameExists(newName) {
count = 0;
canvas.forEachObject(function(obj, i) {
if (obj.name == newName) {
count++;
}
});
if (count > 0) {
return true;
} else {
return false;
}
}
However, after renaming I get an error.
Find below my example:
<html lang="en">
<head>
<link href="https://cdn.jsdelivr.net/npm/bootstrap#5.2.0-beta1/dist/css/bootstrap.min.css" rel="stylesheet" />
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons#1.9.0/font/bootstrap-icons.css" />
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.1/css/all.min.css">
<style>
html,
body {
height: 100%;
width: 100%;
padding: 0;
margin: 0;
}
body {
touch-action: none;
background-image: linear-gradient(to bottom left, rgb(214, 240, 201) 10%, rgba(255, 231, 191, 1) 80%);
-webkit-user-select: none;
-moz-user-select: -moz-none;
-ms-user-select: none;
user-select: none;
}
.input-group {
padding: 4px;
}
.canvas-container {
margin: 0 auto;
width: 100%;
overflow: hidden;
background: url(./transparent.png);
background-size: 15px 15px;
box-shadow: rgba(60, 64, 67, 0.3) 0px 1px 2px 0px, rgba(60, 64, 67, 0.15) 0px 1px 3px 1px;
}
.actived {
background: #fff9a8;
}
#sortable {
max-height: 200px;
overflow: scroll;
}
.list-group {
line-height: 35px;
}
.svg-icon {
width: 1em;
height: 1em;
}
.svg-icon path,
.svg-icon polygon,
.svg-icon rect {
fill: #4691f6;
}
.svg-icon circle {
stroke: #4691f6;
stroke-width: 1;
}
#bkboxXX {
background: url(transparent.png);
border: 1px solid rgba(0, 0, 0, 0.3);
display: inline-block;
height: 19px;
width: 20px;
}
.bk-btn {
background: url(/transparent.png);
}
.fl {
float: left;
}
.fr {
float: right;
}
.input-group-text {
background-color: #f4f4f4;
}
.input-group-sm>.input-group-text {
/*
padding: .2rem .3rem !important;
*/
}
.list-group-item {
padding: 2px 10px;
;
cursor: pointer;
}
.list-group {
border-radius: 0;
text-align: left;
}
</style>
</head>
<body>
<div class="container-fluid h-100">
<div class="row h-100">
<div class="col-sm-3 border-end text-center h-100 overflow-scroll bg-light py-3">
<div class="form-floating w-100 mb-3 tour5">
<h6 class="mb-3">Add Element</h6>
<button class="btn btn-outline-primary" onclick="addText();" data-toggle="tooltip" data-placement="top" title="" data-bs-original-title="Add Text" aria-label="Add Text"><i
class="fa fa-font"></i></button>
<button class="btn btn-outline-primary" data-toggle="tooltip" data-placement="top" title="" onclick=" canvas.add(new fabric.Circle({ name: genNextName(), originX:'center', originY:'center', radius: 30, fill: 'green', top: 100, left: 100 }));" data-bs-original-title="Add Circle"
aria-label="Add Circle"><i
class="fa fa-circle"></i></button>
<button class="btn btn-outline-primary" data-toggle="tooltip" data-placement="top" title="" onclick=" canvas.add(new fabric.Triangle({ name: genNextName(), originX:'center', originY:'center', height:100, width:100, fill: 'red', top: 100, left: 100 }));"
data-bs-original-title="Add Triangle">
<svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="triangle" role="img"
xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" class=""
style="height: 16px;top: 9px;">
<path fill="currentColor"
d="M329.6 24c-18.4-32-64.7-32-83.2 0L6.5 440c-18.4 31.9 4.6 72 41.6 72H528c36.9 0 60-40 41.6-72l-240-416z"
class=""></path>
</svg>
</button>
<button class="btn btn-outline-primary" data-toggle="tooltip" data-placement="top" title="" onclick="canvas.add(new fabric.Rect({ name: genNextName(), originX:'center', originY:'center', left: 110, top: 110, fill: '#000', width: 50, height: 50 }))" data-bs-original-title="Add Rectangle"><i class="fa fa-square-full"></i> </button>
</div>
<hr>
<div class="w-100 tour7 Xd-none Xd-sm-block">
<h6 class="mb-3">Elements</h6>
<ul class="list-group" id="sortable"></ul>
</div>
</div>
<div class="col-sm-6 my-auto py-4 overflow-hidden">
<div class="canvas-container" style="width: 329px; height: 329px; position: relative; user-select: none;"><canvas id="c" class="lower-canvas" width="329" height="329" style="position: absolute; width: 329px; height: 329px; left: 0px; top: 0px; touch-action: none; user-select: none;"></canvas>
</div>
</div>
<div class="col-sm-3 border-start py-3 h-100 overflow-scroll bg-light text-center">
right map
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap#5.2.0-beta1/dist/js/bootstrap.bundle.min.js"></script>
<script src="https://code.jquery.com/jquery-3.6.0.js"></script>
<script src="https://code.jquery.com/ui/1.13.1/jquery-ui.js"></script>
<script src="https://unpkg.com/fabric#5.2.1/dist/fabric.min.js"></script>
<script src="https://rawcdn.githack.com/lyzerk/fabric-history/8c223cbdc8305307b4a8f8710f97da54d9146ffa/src/index.js"></script>
<script src="https://rawgit.com/fabricjs/fabric.js/master/lib/centering_guidelines.js"></script>
<script src="https://rawgit.com/fabricjs/fabric.js/master/lib/aligning_guidelines.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/webfont/1.5.10/webfont.js"></script>
<script>
const rgba2hex = (rgba) => `#${rgba.match(/^rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*(\d+\.{0,1}\d*))?\)$/).slice(1).map((n, i) => (i === 3 ? Math.round(parseFloat(n) * 255) : parseFloat(n)).toString(16).padStart(2, '0').replace('NaN', '')).join('')}`
function cn2h(cn) {
if (!cn) {
return 'transparent';
}
d = document.createElement("span");
d.style.display = "none";
d.style.color = cn
document.body.appendChild(d)
return rgba2hex(window.getComputedStyle(d).color);
}
</script>
<script>
var canvas;
var apiUrl;
var startedLoadingFamilies = false;
var to;
var scale;
fabric.Text.prototype.set({
_getNonTransformedDimensions() { // Object dimensions
return new fabric.Point(this.width, this.height).scalarAdd(this.padding);
},
_calculateCurrentDimensions() { // Controls dimensions
return fabric.util.transformPoint(this._getTransformedDimensions(), this.getViewportTransform(), true);
}
});
function fitCanvas(w, h) {
var scaleW = (document.querySelectorAll(".col-sm-6")[0].offsetWidth - 100) / w;
var scaleH = (document.querySelectorAll("body")[0].offsetHeight - 100) / h;
scale = Math.min(scaleH, scaleW);
if (scale > 1) {
scale = 1;
}
canvas.setZoom(scale);
canvas.setWidth(w * scale);
canvas.setHeight(h * scale);
initAligningGuidelines(canvas);
initCenteringGuidelines(canvas);
canvas.renderAll();
console.log("finished!!");
}
(function() {
canvas = new fabric.Canvas('c', {
allowTouchScrolling: true
});
WebFont.load({
google: {
families: ["Inter:200"]
},
active: function() {
canvas.loadFromJSON({
"height": "500",
"width": "500",
"edit": "yes",
"objects": [],
"backgroundImage": {
"crossOrigin": "anonymous"
}
}, function() {
fitCanvas(500, 500);
})
}
});
canvas.on('before:render', function(opt) {
canvas.getObjects().map(function(o, i) {
if (o.type.toLowerCase() == "textbox") {
var maxH = (o.maxHeight) ? (o.maxHeight) : (canvas.height);
o.set({
'maxHeight': maxH
});
if (o.fontSize > 0 && (maxH > o.fontSize)) {
while (o.height > maxH) {
o.set({
'fontSize': o.fontSize - 1
});
}
}
}
if (o.circleFrame) {
o.set({
clipPath: new fabric.Circle({
radius: o.width / 2,
originX: 'center',
originY: 'center'
})
});
}
if ((o.rx || o.ry) && o.type == "image") {
let rx = (o.rx || 0);
let ry = (o.ry || 0);
o.set({
clipPath: new fabric.Rect({
rx: rx,
ry: ry,
height: o.height,
width: o.width,
originX: 'center',
originY: 'center'
})
});
}
});
});
})();
</script>
<script>
fabric.Object.prototype.objectCaching = false;
function setCanvasBG(clr) {
canvas.backgroundColor = clr;
canvas.renderAll();
}
function changeDim() {
fitCanvas($("#widthC").val(), $("#heightC").val());
}
function setColor(clr, what = 'fill') {
canvas.getActiveObject().set(what, clr);
canvas.renderAll();
}
(function() {
$(document).on("mouseenter", "[data-label]", function() {
$("#font-search").val($(this).data("label"));
loadFont($(this).data("label"));
});
fabric.Canvas.prototype.getItemByAttr = function(attr, name) {
var object = null,
objects = this.getObjects();
for (var i = 0, len = this.size(); i < len; i++) {
if (objects[i][attr] && objects[i][attr] == name) {
object = objects[i];
break;
}
}
return object;
};
$("form#f").hide();
var activeObject;
$("#f input, #f select").on("input", function() {
if (["height", "width", "top", "left", "strokeWidth", "charSpacing"].includes(this.name)) {
canvas.getActiveObject().set(this.name, parseFloat(this.value)).setCoords();
} else {
canvas.getActiveObject().set(this.name, this.value).setCoords();
}
canvas.renderAll();
});
canvas.preserveObjectStacking = true;
fabric.Object.prototype.toObject = (function(toObject) {
return function() {
return fabric.util.object.extend(toObject.call(this), {
name: this.name,
text: this.text,
textAlign: this.textAlign,
fontSize: this.fontSize,
charSpacing: this.charSpacing,
lineHeight: this.lineHeight,
fontWeight: this.fontWeight,
fontFamily: this.fontFamily,
fontStyle: this.fontStyle,
textBackgroundColor: this.textBackgroundColor,
originX: this.originX,
originY: this.originY,
maxHeight: this.maxHeight,
height: this.height,
width: this.width,
radius: this.radius,
rx: this.rx,
ry: this.ry,
stroke: this.stroke,
padding: this.padding,
circleFrame: this.circleFrame
});
};
})(fabric.Object.prototype.toObject);
canvas.on('object:scaling', function(e) {
if (e.target.toObject().type != "image" && e.target.toObject().type != "circle") {
e.target.set({
width: e.target.width * e.target.scaleX,
height: e.target.height * e.target.scaleY,
scaleX: 1,
scaleY: 1
})
}
});
canvas.on('object:modified', function(opt) {
document.body.style.cursor = 'progress';
});
canvas.on("after:render", function() {
$("#sortable").empty();
canvas.includeDefaultValues = false;
canvas.toObject().objects.forEach(function(layer, id) {
if (typeof layer.name !== 'undefined') {
//console.log(id,layer.name);
//canvas.getItemByAttr(`name`, layer.name).set({ "name": String.fromCharCode(65 + id).toLowerCase() })
canvas.getItemByAttr(`name`, layer.name).set({
"name": layer.name.replace(/ /g, "_")
})
var actived = '';
if (canvas.getActiveObject()) {
actived = (canvas.getActiveObject().name == layer.name) ? " actived" : "";
}
$("#sortable").append('<li class="list-group-item ui-sortable-handle clearfix ' + actived + '" onClick="canvas.setActiveObject(canvas.getItemByAttr(`name`,`' + layer.name + '`)); canvas.renderAll();" id=' + layer.name + '>' + layer.name + ' (' + layer.type + ')' + '<span class="fr button-group"><a class="mx-2" title="Edit" href="#" onClick="RenameLayer(`' + layer.name + '`)"><i class="fa fa-pencil-alt" aria-hidden="true"></i></a><a class="mx-2" title="Delete" href="#" onClick="if(confirm(`Are you sure?`)){canvas.remove(canvas.getItemByAttr(`name`,`' + layer.name + '`)); canvas.renderAll();}"><i class="fa fa-trash-alt" aria-hidden="true"></i></a><a class="me-2" title="Duplicate this layer" href="#" onClick="Duplicate(`' + layer.name + '`); canvas.renderAll();"><i class="fa fa-clone" aria-hidden="true"></i></a></span></li>');
}
});
document.body.style.cursor = 'default'
});
canvas.on("selection:created", function(obj) {
if ("image" == obj.selected[0].type) {
canvas.getActiveObject().setControlsVisibility({
mb: false,
ml: false,
mt: false,
mr: false
});
}
$("form#f input[type!='hidden'], #f select").parent().hide();
$("form#f").hide();
canvas.renderAll();
});
canvas.on("selection:updated", function(obj) {
if ("image" == obj.selected[0].type) {
canvas.getActiveObject().setControlsVisibility({
mb: false,
ml: false,
mt: false,
mr: false
});
}
canvas.renderAll();
$("form#f").hide();
$("form#f input[type!='hidden'] , #f select, #f textarea").parent().hide();
});
canvas.on("selection:cleared", function() {
canvas.renderAll();
$("form#f").hide();
$("form#f input[type!='hidden'], #f select, #f textarea").parent().hide();
});
canvas.hoverCursor = 'default';
canvas.on('mouse:over', function(e) {
if (e.target) {
e.target._renderControls(canvas.contextTop, {
hasControls: false
})
}
});
canvas.on('mouse:out', function(e) {
canvas.clearContext(canvas.contextTop);
});
canvas.on('mouse:down', function(e) {
canvas.clearContext(canvas.contextTop);
});
addText = function() {
var text = new fabric.Textbox("Edit this Text", {
name: genNextName(),
left: canvas.getWidth() / canvas.getZoom() / 2,
top: canvas.getHeight() / canvas.getZoom() / 2,
width: (canvas.getWidth() / canvas.getZoom()) / 2,
fill: "#000000",
originX: "center",
originY: "center",
fontFamily: "Inter",
fontWeight: 400,
fontSize: 60,
padding: 20
});
text.setControlsVisibility({
mt: false,
mb: false,
ml: true,
mr: true,
tl: true,
tr: true,
bl: true,
br: true
});
canvas.add(text);
canvas.setActiveObject(text);
canvas.renderAll();
};
})();
function hide_mh_box() {
canvas.remove(canvas.getItemByAttr("isBB", true));
}
function show_mh_box() {
var h = parseInt($("[name=maxHeight]").val());
var n = new fabric.Rect({
top: canvas.getActiveObject().get('top'),
left: canvas.getActiveObject().get('left'),
width: canvas.getActiveObject().get('width'),
height: h,
originX: canvas.getActiveObject().get('originX'),
originY: canvas.getActiveObject().get('originY'),
angle: canvas.getActiveObject().get('angle'),
opacity: 1,
strokeWidth: 2,
stroke: "#FF00FF",
fill: "rgba(0,0,0,0)",
evented: !1,
isBB: true
});
canvas.add(n);
canvas.renderAll();
}
var visited = [];
$(document).ready(function() {
$("input[type=color]").on("input", function() {
$(this).parent().parent().find('input').first().val(this.value);
});
$("textarea, input").attr("autocomplete", "off");
if (window !== window.parent) {
const url = new URL(document.referrer);
$(".isIframe").removeClass('d-none');
$(".notIframe").hide();
}
});
</script>
<script>
function Duplicate(name) {
Copy(name);
}
function Copy(name) {
canvas.getItemByAttr("name", name).clone(function(cloned) {
cloned.set({
"name": genNextName()
})
_clipboard = cloned;
Paste(name);
canvas.renderAll();
});
}
function RenameLayer(name) {
let layerObj = canvas.getItemByAttr("name", name);
let newName = prompt("Rename Layer?", layerObj.name);
while (checkIfNameExists(newName)) {
alert("Your layer name: " + layerObj.name + " is NOT unique please rename your layer differently.")
newName = prompt("Rename Layer?", layerObj.name);
}
layerObj.name = newName;
canvas.renderAll();
}
function checkIfNameExists(newName) {
count = 0;
canvas.forEachObject(function(obj, i) {
if (obj.name == newName) {
count++;
}
});
if (count > 0) {
return true;
} else {
return false;
}
}
function Paste(name) {
_clipboard.clone(function(clonedObj) {
canvas.discardActiveObject();
clonedObj.set({
left: clonedObj.left + 10,
top: clonedObj.top + 10,
evented: true,
});
if (clonedObj.type === 'activeSelection') {
clonedObj.canvas = canvas;
clonedObj.forEachObject(function(obj) {
canvas.add(obj);
});
clonedObj.setCoords();
} else {
canvas.add(clonedObj);
}
_clipboard.top += 10;
_clipboard.left += 10;
canvas.setActiveObject(clonedObj);
canvas.requestRenderAll();
});
}
function genNextName() {
canvas.renderAll();
var total = canvas.getObjects().length;
return String.fromCharCode(65 + total).toLowerCase()
}
fabric.Object.prototype.transparentCorners = false;
fabric.Object.prototype.borderColor = '#5cdce4';
fabric.Object.prototype.cornerColor = 'white';
fabric.Object.prototype.cornerStrokeColor = 'blue';
fabric.Object.prototype.cornerStyle = 'circle';
fabric.Object.prototype.cornerSize = 10;
fabric.Object.prototype.borderScaleFactor = 2;
</script>
</body>
</html>
Any suggestions what I am doing wrong?
I appreciate your replies!
I didn't do deep debugging of the code, but since in many places the layer "name" is used as a key value, you can try renaming the layer (and subsequent updating) in a separate macrotask, using setTimeout.
Instead of this code:
layerObj.name = newName;
canvas.renderAll();
try this code:
setTimeout(() => {
layerObj.name = newName;
canvas.renderAll();
});
Example of your code with setTimeout:
<html lang="en">
<head>
<link href="https://cdn.jsdelivr.net/npm/bootstrap#5.2.0-beta1/dist/css/bootstrap.min.css" rel="stylesheet" />
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons#1.9.0/font/bootstrap-icons.css" />
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.1/css/all.min.css">
<style>
html,
body {
height: 100%;
width: 100%;
padding: 0;
margin: 0;
}
body {
touch-action: none;
background-image: linear-gradient(to bottom left, rgb(214, 240, 201) 10%, rgba(255, 231, 191, 1) 80%);
-webkit-user-select: none;
-moz-user-select: -moz-none;
-ms-user-select: none;
user-select: none;
}
.input-group {
padding: 4px;
}
.canvas-container {
margin: 0 auto;
width: 100%;
overflow: hidden;
background: url(./transparent.png);
background-size: 15px 15px;
box-shadow: rgba(60, 64, 67, 0.3) 0px 1px 2px 0px, rgba(60, 64, 67, 0.15) 0px 1px 3px 1px;
}
.actived {
background: #fff9a8;
}
#sortable {
max-height: 200px;
overflow: scroll;
}
.list-group {
line-height: 35px;
}
.svg-icon {
width: 1em;
height: 1em;
}
.svg-icon path,
.svg-icon polygon,
.svg-icon rect {
fill: #4691f6;
}
.svg-icon circle {
stroke: #4691f6;
stroke-width: 1;
}
#bkboxXX {
background: url(transparent.png);
border: 1px solid rgba(0, 0, 0, 0.3);
display: inline-block;
height: 19px;
width: 20px;
}
.bk-btn {
background: url(/transparent.png);
}
.fl {
float: left;
}
.fr {
float: right;
}
.input-group-text {
background-color: #f4f4f4;
}
.input-group-sm>.input-group-text {
/*
padding: .2rem .3rem !important;
*/
}
.list-group-item {
padding: 2px 10px;
;
cursor: pointer;
}
.list-group {
border-radius: 0;
text-align: left;
}
</style>
</head>
<body>
<div class="container-fluid h-100">
<div class="row h-100">
<div class="col-sm-3 border-end text-center h-100 overflow-scroll bg-light py-3">
<div class="form-floating w-100 mb-3 tour5">
<h6 class="mb-3">Add Element</h6>
<button class="btn btn-outline-primary" onclick="addText();" data-toggle="tooltip" data-placement="top" title="" data-bs-original-title="Add Text" aria-label="Add Text"><i
class="fa fa-font"></i></button>
<button class="btn btn-outline-primary" data-toggle="tooltip" data-placement="top" title="" onclick=" canvas.add(new fabric.Circle({ name: genNextName(), originX:'center', originY:'center', radius: 30, fill: 'green', top: 100, left: 100 }));" data-bs-original-title="Add Circle"
aria-label="Add Circle"><i
class="fa fa-circle"></i></button>
<button class="btn btn-outline-primary" data-toggle="tooltip" data-placement="top" title="" onclick=" canvas.add(new fabric.Triangle({ name: genNextName(), originX:'center', originY:'center', height:100, width:100, fill: 'red', top: 100, left: 100 }));"
data-bs-original-title="Add Triangle">
<svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="triangle" role="img"
xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" class=""
style="height: 16px;top: 9px;">
<path fill="currentColor"
d="M329.6 24c-18.4-32-64.7-32-83.2 0L6.5 440c-18.4 31.9 4.6 72 41.6 72H528c36.9 0 60-40 41.6-72l-240-416z"
class=""></path>
</svg>
</button>
<button class="btn btn-outline-primary" data-toggle="tooltip" data-placement="top" title="" onclick="canvas.add(new fabric.Rect({ name: genNextName(), originX:'center', originY:'center', left: 110, top: 110, fill: '#000', width: 50, height: 50 }))" data-bs-original-title="Add Rectangle"><i class="fa fa-square-full"></i> </button>
</div>
<hr>
<div class="w-100 tour7 Xd-none Xd-sm-block">
<h6 class="mb-3">Elements</h6>
<ul class="list-group" id="sortable"></ul>
</div>
</div>
<div class="col-sm-6 my-auto py-4 overflow-hidden">
<div class="canvas-container" style="width: 329px; height: 329px; position: relative; user-select: none;"><canvas id="c" class="lower-canvas" width="329" height="329" style="position: absolute; width: 329px; height: 329px; left: 0px; top: 0px; touch-action: none; user-select: none;"></canvas>
</div>
</div>
<div class="col-sm-3 border-start py-3 h-100 overflow-scroll bg-light text-center">
right map
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap#5.2.0-beta1/dist/js/bootstrap.bundle.min.js"></script>
<script src="https://code.jquery.com/jquery-3.6.0.js"></script>
<script src="https://code.jquery.com/ui/1.13.1/jquery-ui.js"></script>
<script src="https://unpkg.com/fabric#5.2.1/dist/fabric.min.js"></script>
<script src="https://rawcdn.githack.com/lyzerk/fabric-history/8c223cbdc8305307b4a8f8710f97da54d9146ffa/src/index.js"></script>
<script src="https://rawgit.com/fabricjs/fabric.js/master/lib/centering_guidelines.js"></script>
<script src="https://rawgit.com/fabricjs/fabric.js/master/lib/aligning_guidelines.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/webfont/1.5.10/webfont.js"></script>
<script>
const rgba2hex = (rgba) => `#${rgba.match(/^rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*(\d+\.{0,1}\d*))?\)$/).slice(1).map((n, i) => (i === 3 ? Math.round(parseFloat(n) * 255) : parseFloat(n)).toString(16).padStart(2, '0').replace('NaN', '')).join('')}`
function cn2h(cn) {
if (!cn) {
return 'transparent';
}
d = document.createElement("span");
d.style.display = "none";
d.style.color = cn
document.body.appendChild(d)
return rgba2hex(window.getComputedStyle(d).color);
}
</script>
<script>
var canvas;
var apiUrl;
var startedLoadingFamilies = false;
var to;
var scale;
fabric.Text.prototype.set({
_getNonTransformedDimensions() { // Object dimensions
return new fabric.Point(this.width, this.height).scalarAdd(this.padding);
},
_calculateCurrentDimensions() { // Controls dimensions
return fabric.util.transformPoint(this._getTransformedDimensions(), this.getViewportTransform(), true);
}
});
function fitCanvas(w, h) {
var scaleW = (document.querySelectorAll(".col-sm-6")[0].offsetWidth - 100) / w;
var scaleH = (document.querySelectorAll("body")[0].offsetHeight - 100) / h;
scale = Math.min(scaleH, scaleW);
if (scale > 1) {
scale = 1;
}
canvas.setZoom(scale);
canvas.setWidth(w * scale);
canvas.setHeight(h * scale);
initAligningGuidelines(canvas);
initCenteringGuidelines(canvas);
canvas.renderAll();
console.log("finished!!");
}
(function() {
canvas = new fabric.Canvas('c', {
allowTouchScrolling: true
});
WebFont.load({
google: {
families: ["Inter:200"]
},
active: function() {
canvas.loadFromJSON({
"height": "500",
"width": "500",
"edit": "yes",
"objects": [],
"backgroundImage": {
"crossOrigin": "anonymous"
}
}, function() {
fitCanvas(500, 500);
})
}
});
canvas.on('before:render', function(opt) {
canvas.getObjects().map(function(o, i) {
if (o.type.toLowerCase() == "textbox") {
var maxH = (o.maxHeight) ? (o.maxHeight) : (canvas.height);
o.set({
'maxHeight': maxH
});
if (o.fontSize > 0 && (maxH > o.fontSize)) {
while (o.height > maxH) {
o.set({
'fontSize': o.fontSize - 1
});
}
}
}
if (o.circleFrame) {
o.set({
clipPath: new fabric.Circle({
radius: o.width / 2,
originX: 'center',
originY: 'center'
})
});
}
if ((o.rx || o.ry) && o.type == "image") {
let rx = (o.rx || 0);
let ry = (o.ry || 0);
o.set({
clipPath: new fabric.Rect({
rx: rx,
ry: ry,
height: o.height,
width: o.width,
originX: 'center',
originY: 'center'
})
});
}
});
});
})();
</script>
<script>
fabric.Object.prototype.objectCaching = false;
function setCanvasBG(clr) {
canvas.backgroundColor = clr;
canvas.renderAll();
}
function changeDim() {
fitCanvas($("#widthC").val(), $("#heightC").val());
}
function setColor(clr, what = 'fill') {
canvas.getActiveObject().set(what, clr);
canvas.renderAll();
}
(function() {
$(document).on("mouseenter", "[data-label]", function() {
$("#font-search").val($(this).data("label"));
loadFont($(this).data("label"));
});
fabric.Canvas.prototype.getItemByAttr = function(attr, name) {
var object = null,
objects = this.getObjects();
for (var i = 0, len = this.size(); i < len; i++) {
if (objects[i][attr] && objects[i][attr] == name) {
object = objects[i];
break;
}
}
return object;
};
$("form#f").hide();
var activeObject;
$("#f input, #f select").on("input", function() {
if (["height", "width", "top", "left", "strokeWidth", "charSpacing"].includes(this.name)) {
canvas.getActiveObject().set(this.name, parseFloat(this.value)).setCoords();
} else {
canvas.getActiveObject().set(this.name, this.value).setCoords();
}
canvas.renderAll();
});
canvas.preserveObjectStacking = true;
fabric.Object.prototype.toObject = (function(toObject) {
return function() {
return fabric.util.object.extend(toObject.call(this), {
name: this.name,
text: this.text,
textAlign: this.textAlign,
fontSize: this.fontSize,
charSpacing: this.charSpacing,
lineHeight: this.lineHeight,
fontWeight: this.fontWeight,
fontFamily: this.fontFamily,
fontStyle: this.fontStyle,
textBackgroundColor: this.textBackgroundColor,
originX: this.originX,
originY: this.originY,
maxHeight: this.maxHeight,
height: this.height,
width: this.width,
radius: this.radius,
rx: this.rx,
ry: this.ry,
stroke: this.stroke,
padding: this.padding,
circleFrame: this.circleFrame
});
};
})(fabric.Object.prototype.toObject);
canvas.on('object:scaling', function(e) {
if (e.target.toObject().type != "image" && e.target.toObject().type != "circle") {
e.target.set({
width: e.target.width * e.target.scaleX,
height: e.target.height * e.target.scaleY,
scaleX: 1,
scaleY: 1
})
}
});
canvas.on('object:modified', function(opt) {
document.body.style.cursor = 'progress';
});
canvas.on("after:render", function() {
$("#sortable").empty();
canvas.includeDefaultValues = false;
canvas.toObject().objects.forEach(function(layer, id) {
if (typeof layer.name !== 'undefined') {
//console.log(id,layer.name);
//canvas.getItemByAttr(`name`, layer.name).set({ "name": String.fromCharCode(65 + id).toLowerCase() })
canvas.getItemByAttr(`name`, layer.name).set({
"name": layer.name.replace(/ /g, "_")
})
var actived = '';
if (canvas.getActiveObject()) {
actived = (canvas.getActiveObject().name == layer.name) ? " actived" : "";
}
$("#sortable").append('<li class="list-group-item ui-sortable-handle clearfix ' + actived + '" onClick="canvas.setActiveObject(canvas.getItemByAttr(`name`,`' + layer.name + '`)); canvas.renderAll();" id=' + layer.name + '>' + layer.name + ' (' + layer.type + ')' + '<span class="fr button-group"><a class="mx-2" title="Edit" href="#" onClick="RenameLayer(`' + layer.name + '`)"><i class="fa fa-pencil-alt" aria-hidden="true"></i></a><a class="mx-2" title="Delete" href="#" onClick="if(confirm(`Are you sure?`)){canvas.remove(canvas.getItemByAttr(`name`,`' + layer.name + '`)); canvas.renderAll();}"><i class="fa fa-trash-alt" aria-hidden="true"></i></a><a class="me-2" title="Duplicate this layer" href="#" onClick="Duplicate(`' + layer.name + '`); canvas.renderAll();"><i class="fa fa-clone" aria-hidden="true"></i></a></span></li>');
}
});
document.body.style.cursor = 'default'
});
canvas.on("selection:created", function(obj) {
if ("image" == obj.selected[0].type) {
canvas.getActiveObject().setControlsVisibility({
mb: false,
ml: false,
mt: false,
mr: false
});
}
$("form#f input[type!='hidden'], #f select").parent().hide();
$("form#f").hide();
canvas.renderAll();
});
canvas.on("selection:updated", function(obj) {
if ("image" == obj.selected[0].type) {
canvas.getActiveObject().setControlsVisibility({
mb: false,
ml: false,
mt: false,
mr: false
});
}
canvas.renderAll();
$("form#f").hide();
$("form#f input[type!='hidden'] , #f select, #f textarea").parent().hide();
});
canvas.on("selection:cleared", function() {
canvas.renderAll();
$("form#f").hide();
$("form#f input[type!='hidden'], #f select, #f textarea").parent().hide();
});
canvas.hoverCursor = 'default';
canvas.on('mouse:over', function(e) {
if (e.target) {
e.target._renderControls(canvas.contextTop, {
hasControls: false
})
}
});
canvas.on('mouse:out', function(e) {
canvas.clearContext(canvas.contextTop);
});
canvas.on('mouse:down', function(e) {
canvas.clearContext(canvas.contextTop);
});
addText = function() {
var text = new fabric.Textbox("Edit this Text", {
name: genNextName(),
left: canvas.getWidth() / canvas.getZoom() / 2,
top: canvas.getHeight() / canvas.getZoom() / 2,
width: (canvas.getWidth() / canvas.getZoom()) / 2,
fill: "#000000",
originX: "center",
originY: "center",
fontFamily: "Inter",
fontWeight: 400,
fontSize: 60,
padding: 20
});
text.setControlsVisibility({
mt: false,
mb: false,
ml: true,
mr: true,
tl: true,
tr: true,
bl: true,
br: true
});
canvas.add(text);
canvas.setActiveObject(text);
canvas.renderAll();
};
})();
function hide_mh_box() {
canvas.remove(canvas.getItemByAttr("isBB", true));
}
function show_mh_box() {
var h = parseInt($("[name=maxHeight]").val());
var n = new fabric.Rect({
top: canvas.getActiveObject().get('top'),
left: canvas.getActiveObject().get('left'),
width: canvas.getActiveObject().get('width'),
height: h,
originX: canvas.getActiveObject().get('originX'),
originY: canvas.getActiveObject().get('originY'),
angle: canvas.getActiveObject().get('angle'),
opacity: 1,
strokeWidth: 2,
stroke: "#FF00FF",
fill: "rgba(0,0,0,0)",
evented: !1,
isBB: true
});
canvas.add(n);
canvas.renderAll();
}
var visited = [];
$(document).ready(function() {
$("input[type=color]").on("input", function() {
$(this).parent().parent().find('input').first().val(this.value);
});
$("textarea, input").attr("autocomplete", "off");
if (window !== window.parent) {
const url = new URL(document.referrer);
$(".isIframe").removeClass('d-none');
$(".notIframe").hide();
}
});
</script>
<script>
function Duplicate(name) {
Copy(name);
}
function Copy(name) {
canvas.getItemByAttr("name", name).clone(function(cloned) {
cloned.set({
"name": genNextName()
})
_clipboard = cloned;
Paste(name);
canvas.renderAll();
});
}
function RenameLayer(name) {
let layerObj = canvas.getItemByAttr("name", name);
let newName = prompt("Rename Layer?", layerObj.name);
while (checkIfNameExists(newName)) {
alert("Your layer name: " + layerObj.name + " is NOT unique please rename your layer differently.")
newName = prompt("Rename Layer?", layerObj.name);
}
setTimeout(() => {
layerObj.name = newName;
canvas.renderAll();
})
}
function checkIfNameExists(newName) {
count = 0;
canvas.forEachObject(function(obj, i) {
if (obj.name == newName) {
count++;
}
});
if (count > 0) {
return true;
} else {
return false;
}
}
function Paste(name) {
_clipboard.clone(function(clonedObj) {
canvas.discardActiveObject();
clonedObj.set({
left: clonedObj.left + 10,
top: clonedObj.top + 10,
evented: true,
});
if (clonedObj.type === 'activeSelection') {
clonedObj.canvas = canvas;
clonedObj.forEachObject(function(obj) {
canvas.add(obj);
});
clonedObj.setCoords();
} else {
canvas.add(clonedObj);
}
_clipboard.top += 10;
_clipboard.left += 10;
canvas.setActiveObject(clonedObj);
canvas.requestRenderAll();
});
}
function genNextName() {
canvas.renderAll();
var total = canvas.getObjects().length;
return String.fromCharCode(65 + total).toLowerCase()
}
fabric.Object.prototype.transparentCorners = false;
fabric.Object.prototype.borderColor = '#5cdce4';
fabric.Object.prototype.cornerColor = 'white';
fabric.Object.prototype.cornerStrokeColor = 'blue';
fabric.Object.prototype.cornerStyle = 'circle';
fabric.Object.prototype.cornerSize = 10;
fabric.Object.prototype.borderScaleFactor = 2;
</script>
</body>
</html>
This solution is not optimal. But to be honest, the whole code needs serious refactoring. However, perhaps such a solution at the moment will be able to suit you.
through WebSocket fetching the data from The developers.binary but canvasjs is rendering to the charts taking too much time but I saw that a lot of charts all are rendering normally not taking much time. I want to know that why it happens
what should I do to render the charts quickly?
I need to visible the charts quickly
is it taking time to the API call or I've mistaken anything?
window.addEventListener('load', function(e) {
var ws, b, rnd, spot, time, dps, xd, digit, cnt, random, id, lng, str, chart, xVal, yVal, mType, mColor, rndMenu;
ws = new WebSocket("wss://ws.binaryws.com/websockets/v3?app_id=3738&l=" + lng);
str = ["R_100", "R_10", "R_25", "R_50", "R_75", "RDBEAR", "RDBULL"];
dps = [];
time = [0];
spot = [0];
digit = [0];
mType = "none";
mColor = "#32cd32";
lng = "EN";
xVal = 0;
yVal = 0;
cnt = 20;
rndMenu = document.querySelectorAll('div.menu > span');
function toggleDigit(d, m) {
var nameClass = document.querySelector("#digits > span:nth-child(" + d + ")").className;
if (nameClass != "digits_moved_" + m) {
document.querySelector("#digits > span:nth-child(" + d + ")").classList.remove(nameClass);
document.querySelector("#digits > span:nth-child(" + d + ")").classList.add("digits_moved_" + m);
}
}
function rndGet() {
random = document.querySelector("body > div.menu > span.menu-active").title;
rnd = "R_10";
xd = 3;
}
rndGet();
ws.onopen = function(evt) {
ws.send(JSON.stringify({
ticks: rnd
}));
};
ws.onmessage = function(msg) {
b = JSON.parse(msg.data);
if (b.tick) {
if (b.echo_req.ticks == rnd) {
id = b.tick.id;
ws.send(JSON.stringify({
ticks_history: rnd,
end: "latest",
start: 1,
style: "ticks",
count: cnt + 1
}));
} else {
ws.send(JSON.stringify({
forget: id
}));
ws.send(JSON.stringify({
forget_all: "ticks"
}));
ws.send(JSON.stringify({
ticks: rnd
}));
};
};
if (b.history) {
if (b.echo_req.ticks_history == rnd) {
for (var i = 0; i < cnt + 1; i++) {
time[i] = b.history.times[cnt - i];
spot[i] = b.history.prices[cnt - i];
spot[i] = Number(spot[i]).toFixed(xd);
digit[i] = spot[i].slice(-1);
}
for (var i = 0; i < cnt + 1; i++) {
xVal = new Date(time[i] * 1000);
yVal = parseFloat(spot[i]);
if (i == 0) mType = "circle";
else mType = "none";
if (yVal == Math.max.apply(null, spot)) {
mColor = "#29abe2";
mType = "circle";
} else if (yVal == Math.min.apply(null, spot)) {
mColor = "#c03";
mType = "circle";
} else {
mColor = "#32cd32";
}
dps.push({
x: xVal,
y: yVal,
markerType: mType,
markerColor: mColor,
markerBorderColor: "#ccc"
});
}
chart.render()
spot.reverse();
digit.reverse();
for (var i = 1; i < cnt + 1; i++) {
document.querySelector("#digits > span:nth-child(" + i + ")").innerHTML = digit[i];
if (spot[i - 1] < spot[i]) {
toggleDigit(i, "up");
} else if (spot[i - 1] > spot[i]) {
toggleDigit(i, "down");
} else if (spot[i - 1] == spot[i] && i - 1 > 0) {
if (document.querySelector("#digits > span:nth-child(" + (i - 1) + ")").className == "digits_moved_up") {
toggleDigit(i, "up");
} else if (document.querySelector("#digits > span:nth-child(" + (i - 1) + ")").className == "digits_moved_down") {
toggleDigit(i, "down");
}
}
}
};
};
};
chart = new CanvasJS.Chart("chartContainer", {
animationEnabled: false,
theme: "light2",
title: {
titleFontSize: 0,
text: ""
},
toolTip: {
enabled: true,
animationEnabled: true,
borderColor: "#ccc",
borderThickness: 1,
fontColor: "#000",
content: "{y}"
},
axisX: {
includeZero: false,
titleFontSize: 0,
labelFontSize: 0,
gridThickness: 0,
tickLength: 0,
lineThickness: 0
},
axisY: {
includeZero: false,
titleFontSize: 0,
labelFontSize: 0,
gridThickness: 0,
tickLength: 0,
lineThickness: 0
},
data: [{
type: "spline",
lineColor: "#ccc",
lineThickness: 2,
markerType: "none",
markerSize: 5,
markerBorderThickness: 0,
dataPoints: dps
}],
});
e.preventDefault()
}, false);
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Asim chart</title>
<style>
body {
margin: 0;
padding: 0 10px;
font-size: 16px;
text-align: center;
font-family: "Arial", sans-serif;
}
h1 {
font-size: 34px;
}
.menu {
margin-bottom: 16px;
display: flex;
}
.menu span {
padding: 3px 0;
flex-basis: auto;
flex-grow: 1;
text-align: center;
font-size: 16px;
border-radius: 5px;
color: #fff;
background-color: #666666;
}
.menu span:hover {
background: #191919;
}
span.menu-active {
background: #191919;
}
.digits {
margin-bottom: 20px;
display: flex;
}
.disclaimer {
font-size: 11px;
text-align: center;
font-family: "Arial", sans-serif;
color: #707070;
}
.digits span {
padding: 25px 0;
flex-basis: auto;
flex-grow: 1;
text-align: center;
font-size: 24px;
border-radius: 15px;
color: #fff;
}
.digits_moved_down {
background-color: #c03;
}
.digits_moved_up {
background-color: #29abe2;
}
.chartContainer {
min-height: 350px;
min-width: 50px;
}
</style>
<script type="text/javascript" src="https://canvasjs.com/assets/script/canvasjs.min.js"></script>
<script type="text/javascript" src="script.js"></script>
</head>
<body class="EN">
<h1>charts</h1>
<h3>charts difference</h3>
<div class="menu">
<span class="" title="R_100">100</span>
<span class="menu-active" title="R_10">10</span>
<span class="" title="R_25">25</span>
<span class="" title="R_50">50</span>
<span class="" title="R_75">75</span>
<span class="" title="RDBEAR">ODD/EVEN</span>
<span class="" title="RDBULL">MATCH/DIFFERS</span>
</div>
<p>Charts might take up to 30 secs to load.. please wait...</p>
<div id="digits" class="digits">
<span class="_"></span>
<span class="_"></span>
<span class="_"></span>
<span class="_"></span>
<span class="_"></span>
<span class="_"></span>
<span class="_"></span>
<span class="_"></span>
<span class="_"></span>
<span class="_"></span>
<span class="_"></span>
<span class="_"></span>
<span class="_"></span>
<span class="_"></span>
<span class="_"></span>
<span class="_"></span>
<span class="_"></span>
<span class="_"></span>
<span class="_"></span>
<span class="_"></span>
</div>
<div class="chartContainer" id="chartContainer"></div>
<div class="disclaimer">
</div>
</body>
</html>
Chart is getting rendered within 20-30 milliseconds. However, API call & the response seems to be taking 3-5 secs. Below is the code showcasing the time taken:
window.addEventListener('load', function (e) {
var timeNow;
var ws, b, rnd, spot, time, dps, xd, digit, cnt, random, id, lng, str, chart, xVal, yVal, mType, mColor, rndMenu;
ws = new WebSocket("wss://ws.binaryws.com/websockets/v3?app_id=3738&l=" + lng);
str = ["R_100", "R_10", "R_25", "R_50", "R_75", "RDBEAR", "RDBULL"];
dps = [];
time = [0];
spot = [0];
digit = [0];
mType = "none";
mColor = "#32cd32";
lng = "EN";
xVal = 0;
yVal = 0;
cnt = 20;
rndMenu = document.querySelectorAll('div.menu > span');
function toggleDigit(d, m) {
var nameClass = document.querySelector("#digits > span:nth-child(" + d + ")").className;
if (nameClass != "digits_moved_" + m) {
document.querySelector("#digits > span:nth-child(" + d + ")").classList.remove(nameClass);
document.querySelector("#digits > span:nth-child(" + d + ")").classList.add("digits_moved_" + m);
}
}
function rndGet() {
random = document.querySelector("body > div.menu > span.menu-active").title;
rnd = "R_10";
xd = 3;
}
rndGet();
ws.onopen = function (evt) {
ws.send(JSON.stringify({
ticks: rnd
}));
};
ws.onmessage = function (msg) {
b = JSON.parse(msg.data);
if (b.tick) {
if (b.echo_req.ticks == rnd) {
id = b.tick.id;
ws.send(JSON.stringify({
ticks_history: rnd,
end: "latest",
start: 1,
style: "ticks",
count: cnt + 1
}));
} else {
ws.send(JSON.stringify({
forget: id
}));
ws.send(JSON.stringify({
forget_all: "ticks"
}));
ws.send(JSON.stringify({
ticks: rnd
}));
};
};
if (b.history) {
if (b.echo_req.ticks_history == rnd) {
for (var i = 0; i < cnt + 1; i++) {
time[i] = b.history.times[cnt - i];
spot[i] = b.history.prices[cnt - i];
spot[i] = Number(spot[i]).toFixed(xd);
digit[i] = spot[i].slice(-1);
}
for (var i = 0; i < cnt + 1; i++) {
xVal = new Date(time[i] * 1000);
yVal = parseFloat(spot[i]);
if (i == 0) mType = "circle";
else mType = "none";
if (yVal == Math.max.apply(null, spot)) {
mColor = "#29abe2";
mType = "circle";
} else if (yVal == Math.min.apply(null, spot)) {
mColor = "#c03";
mType = "circle";
} else {
mColor = "#32cd32";
}
dps.push({
x: xVal,
y: yVal,
markerType: mType,
markerColor: mColor,
markerBorderColor: "#ccc"
});
}
var startTime = new Date().getTime();
chart.render();
console.log("Chart rendered in: ", new Date().getTime() - startTime + "ms");
spot.reverse();
digit.reverse();
for (var i = 1; i < cnt + 1; i++) {
document.querySelector("#digits > span:nth-child(" + i + ")").innerHTML = digit[i];
if (spot[i - 1] < spot[i]) {
toggleDigit(i, "up");
} else if (spot[i - 1] > spot[i]) {
toggleDigit(i, "down");
} else if (spot[i - 1] == spot[i] && i - 1 > 0) {
if (document.querySelector("#digits > span:nth-child(" + (i - 1) + ")").className == "digits_moved_up") {
toggleDigit(i, "up");
} else if (document.querySelector("#digits > span:nth-child(" + (i - 1) + ")").className == "digits_moved_down") {
toggleDigit(i, "down");
}
}
}
};
};
};
chart = new CanvasJS.Chart("chartContainer", {
animationEnabled: false,
theme: "light2",
title: {
titleFontSize: 0,
text: ""
},
toolTip: {
enabled: true,
animationEnabled: true,
borderColor: "#ccc",
borderThickness: 1,
fontColor: "#000",
content: "{y}"
},
axisX: {
includeZero: false,
titleFontSize: 0,
labelFontSize: 0,
gridThickness: 0,
tickLength: 0,
lineThickness: 0
},
axisY: {
includeZero: false,
titleFontSize: 0,
labelFontSize: 0,
gridThickness: 0,
tickLength: 0,
lineThickness: 0
},
data: [{
type: "spline",
lineColor: "#ccc",
lineThickness: 2,
markerType: "none",
markerSize: 5,
markerBorderThickness: 0,
dataPoints: dps
}]
});
e.preventDefault()
}, false);
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Asim chart</title>
<style>
body {
margin: 0;
padding: 0 10px;
font-size: 16px;
text-align: center;
font-family: "Arial", sans-serif;
}
h1 {
font-size: 34px;
}
.menu {
margin-bottom: 16px;
display: flex;
}
.menu span {
padding: 3px 0;
flex-basis: auto;
flex-grow: 1;
text-align: center;
font-size: 16px;
border-radius: 5px;
color: #fff;
background-color: #666666;
}
.menu span:hover {
background: #191919;
}
span.menu-active {
background: #191919;
}
.digits {
margin-bottom: 20px;
display: flex;
}
.disclaimer {
font-size: 11px;
text-align: center;
font-family: "Arial", sans-serif;
color: #707070;
}
.digits span {
padding: 25px 0;
flex-basis: auto;
flex-grow: 1;
text-align: center;
font-size: 24px;
border-radius: 15px;
color: #fff;
}
.digits_moved_down {
background-color: #c03;
}
.digits_moved_up {
background-color: #29abe2;
}
.chartContainer {
min-height: 350px;
min-width: 50px;
}
</style>
<script type="text/javascript" src="https://canvasjs.com/assets/script/canvasjs.min.js"></script>
<script type="text/javascript" src="script.js"></script>
</head>
<body class="EN">
<h1>charts</h1>
<h3>charts difference</h3>
<div class="menu">
<span class="" title="R_100">100</span>
<span class="menu-active" title="R_10">10</span>
<span class="" title="R_25">25</span>
<span class="" title="R_50">50</span>
<span class="" title="R_75">75</span>
<span class="" title="RDBEAR">ODD/EVEN</span>
<span class="" title="RDBULL">MATCH/DIFFERS</span>
</div>
<p>Charts might take up to 30 secs to load.. please wait...</p>
<div id="digits" class="digits">
<span class="_"></span>
<span class="_"></span>
<span class="_"></span>
<span class="_"></span>
<span class="_"></span>
<span class="_"></span>
<span class="_"></span>
<span class="_"></span>
<span class="_"></span>
<span class="_"></span>
<span class="_"></span>
<span class="_"></span>
<span class="_"></span>
<span class="_"></span>
<span class="_"></span>
<span class="_"></span>
<span class="_"></span>
<span class="_"></span>
<span class="_"></span>
<span class="_"></span>
</div>
<div class="chartContainer" id="chartContainer"></div>
<div class="disclaimer">
</div>
</body>
</html>
Is that possible to change the opacity of not hovered points in Highcharts, without change the default opacity?
I have this columns charts with opacity: 1 for all columns by default. But I want to set all columns with opacity: 0.3, except the hovered one, when user hover a column, like in this example:
Chart without hover in any column:
Chart with hover:
Here is the code:
Html:
this.graphic = Highcharts.chart('graph-roa', {
chart: {
zoomType: 'xy',
marginTop: 20
},
title: {
text: undefined
},
credits: {
text: '',
href: ''
},
xAxis: [{
categories: ['13/07','14/07','15/07','16/07','17/07','20/07', '21/07']
}],
yAxis: [
{
lineWidth: 1,
gridLineWidth: 0,
labels: {
formatter() {
return this.value;
},
style: {
color: Highcharts.getOptions().colors[1]
}
},
title: {
align: 'high',
offset: 3,
text: 'Reais',
rotation: 0,
y: -10
}
},
{
title: {
text: ''
},
labels: {
format: '',
style: {
color: '#fff'
}
},
opposite: true
}
],
tooltip: {
shared: true,
},
legend: {
layout: 'vertical',
align: 'left',
x: 700,
verticalAlign: 'top',
y: 10,
floating: true,
backgroundColor: ('#000' && '#fff') || 'rgba(255,255,255,0.25)'
},
series: [{
name: 'Valor',
type: 'column',
color: '#106D98',
data: [20,60,40,100,20,20,20],
cursor: 'pointer',
}]
});
.graph {
// width: auto;
height: 180px;
display: block;
margin-top: 20px;
}
.demais-dados-roa {
font-family: $tt-norms-regular;
font-size: 14px;
margin-bottom: 2em;
.label {
color: $sec-5;
}
<script src="https://code.highcharts.com/highcharts.js"></script>
<div class="row demais-dados-roa justify-content-between atual no-gutters">
<div class="col-auto">
<h3 class="graphic-title">
Repasse Esc.
</h3>
<em class="fas fa-info-circle" (click)="tooltipVisible = true"></em>
</div>
<div class="col-auto">
<div class="row no-gutters">
<h3 class="graphic-title ml-auto">
1,50k
</h3>
</div>
<div class="row no-gutters">
<p class="label">
Acum. Julho
</p>
</div>
</div>
</div>
<div id="graph-roa" class="graph"></div>
You can achieve it by using the mouseOver and mouseOut callbacks to changes the opacity of the points.
Demo: https://jsfiddle.net/BlackLabel/1srupnxk/
point: {
events: {
mouseOver() {
let series = this.series;
series.points.forEach(p => {
p.graphic.css({
opacity: 0.3
})
})
this.graphic.css({
opacity: 1
})
},
mouseOut() {
let series = this.series;
series.points.forEach(p => {
p.graphic.css({
opacity: 1
})
})
}
}
},
API: https://api.highcharts.com/highcharts/series.column.point.events.mouseOver
Here's the code.
The idea is:
.outer:hover > * { opacity: 0.4; } here all the red items are slightly faded on hover.
Now .outer:hover > *:hover is to style the current item that is hovered.
.outer {
display: inline-block;
}
.inner {
height: 100px;
width: 20px;
background: red;
display: inline-block;
margin-right: 10px;
}
.outer:hover > * {
opacity: 0.4;
}
.outer:hover > *:hover {
transform: scale(1.1);
opacity: 1;
}
<div class="outer">
<div class="inner"></div>
<div class="inner"></div>
<div class="inner"></div>
<div class="inner"></div>
<div class="inner"></div>
<div class="inner"></div>
</div>
I have 4 pie charts next to each other in a bootstrap grid which all show well in IE11 document modes IE5, IE7, IE9+, but not on IE8.
What it does in document mode IE8:
When I refresh the page with ctrl+f5: the charts show correct. When I refresh with f5: the charts have a way to big container and display out of screen (and way out of the grid)
My JS:
$(function () {
var chart;
var options = {
chart: {
plotBackgroundColor: null,
plotBorderWidth: null,
plotShadow: false,
height: 200
},
legend: {
align: 'right',
verticalAlign: 'top',
x: 0,
y: 40,
layout: 'vertical'
},
credits: {
enabled: false
},
title: {
margin: 30,
style: {
color: '#6e685c',
fontSize: '10px'
}
},
tooltip: {
pointFormat: 'Aantal: <b>{point.y}</b>'
},
plotOptions: {
pie: {
allowPointSelect: true,
cursor: 'pointer',
size:'100%',
dataLabels: {
enabled: true,
color: '#454545',
format: '{point.y}',
distance: 10
}
}
}
}
$(document).ready(function () {
// Build the chart
options.title.text = 'Financieel fout';
options.series = [{
type: 'pie',
name: 'Kwaliteit',
data: [{
name: 'Goed',
color: '#93ab48',
y: 3
}, {
name: 'Fout',
color: '#ac4742',
y: 3
}, {
name: 'Onzeker',
color: '#e09647',
y: 1
}]
}];
$('#graph-1').highcharts(options);
// Build the chart
options.title.text = 'Financieel onzeker';
options.series = [{
type: 'pie',
name: 'Kwaliteit',
data: [{
name: 'Goed',
color: '#93ab48',
y: 3
},{
name: 'Fout',
color: '#ac4742',
y: 1
}]
}];
$('#graph-2').highcharts(options);
// Build the chart
options.title.text = 'Service desk';
options.series = [{
type: 'pie',
name: 'Kwaliteit',
data: [{
name: 'Goed',
color: '#93ab48',
y: 3
}, {
name: 'Fout',
color: '#ac4742',
y: 3
}, {
name: 'Onzeker',
color: '#e09647',
y: 1
}]
}];
$('#graph-3').highcharts(options);
// Build the chart
options.title.text = 'Controle';
options.series = [{
type: 'pie',
name: 'Kwaliteit',
data: [{
name: 'Goed',
color: '#93ab48',
y: 3
},{
name: 'Fout',
color: '#ac4742',
y: 1
}]
}];
$('#graph-4').highcharts(options);
});
$("body").width($("body").width()-200).delay(1000).width($("body").width()+200);
});
The HTML:
<div class="row">
<div class="col-md-6 graph-container border-right">
<h4>Rechtmatigheid</h4>
<div class="clearfix">
<div id="graph-1" class="col-md-6 no-horizontal-padding"></div>
<div id="graph-2" class="col-md-6 no-horizontal-padding border-right"></div>
</div>
</div>
<div class="col-md-3 graph-container border-right no-horizontal-padding">
<h4 class="padding-left">Kwaliteit</h4>
<div class="graph-container">
<div id="graph-3" class=""></div>
</div>
</div>
<div class="col-md-3 graph-container no-horizontal-padding">
<h4 class="padding-left">Workload</h4>
<div class="graph-container">
<div id="graph-4" class="no-horizontal-padding"></div>
</div>
</div>
</div>
And the CSS
/***************************************************************/
/* GRAPHS */
/***************************************************************/
.graph-container{
}
.graph-container.border-right{
border-right: 1px solid #e5e5e5;
}
.graph-container .graphs-info{
margin: 20px 0px;
}
.graphs-info{
color: #6e685c;
line-height: 26px;
}
.graphs-info .content-title{
font-size: 17px;
font-weight: bold;
display: block;
margin-bottom: 20px;
}
.graphs-info .graphs-info-title{
display: inline-block;
margin-right: 6px;
}
.graphs-info .error{
color: #830020;
}
.padding-left{
padding-left: 20px !important;
}
Do I do something wrong or is this a bug? And will this occur in IE8 (actual browser)
Use comments in HTML for IE8, like this:
<!--[if IE 8]>
<style>...</style>
<![endif]-->
The bug didn't occur on IE8 running on Host OS Windows XP. Therefor the problem is actually non-existent. Another heads up that I should really test on actual IE8 and not the document modes.