I have a problem with the canvas in javscript, I have a function that passes four values to another function as soon as I click an image, each image passes different values, these values I use to draw the chart with rectangles on the canvas, and it works! But as soon as I click another image, the rectangle values of the graph do not change, they remain the same.
Where's the problem?
html
<canvas id="canvas" width="500" height="250"></canvas>
javascript
function gestoreInformazioni(){
for (var i = 0; i < pizzeRegioni.length; i++){
var pizza = pizzeRegioni[i];
if( this.id === pizza.nome ){
generaGrafico(pizza.prezzo, pizza.carboidrati, pizza.grassi, pizza.proteine);
}
}
}
function generaGrafico(prezzo, carboidrati, grassi, proteine){
try{
var ctx = canvas.getContext("2d");
// ctx.beginPath();
ctx.fillStyle='rgb(255,255,0)';
ctx.translate(0,200);
ctx.fillRect(10,-(prezzo),40,150);
ctx.fillRect(100,-(carboidrati),40,150);
ctx.fillRect(200,-(grassi),40,150);
ctx.fillRect(300,-(proteine),40,150);
}catch(e){
alert("generaGrafico " + e);
}
}
init
nodoImgPizza = document.getElementsByClassName("img_pizza");
for(var i = 0; i<nodoImgPizza.length; i++){
nodoImgPizza[i].onclick = gestoreInformazioni;
}
accociativeArray
var pizzeRegioni=[
{
nome:"Lardellata",
regione:"Toscana",
ingredienti :["pomodoro","mozzarella","lardo di Colonnata","ciliegini","porcini"],
minX:134,
minY:128,
maxX:202,
maxY:222,
prezzo:6.50,
carboidrati : 10,
grassi : 15,
proteine : 20,
},
{
nome:"Senese",
regione:"Toscana",
ingredienti :["pomodoro","mozzarella","crudo di cinta senese","porcini"],
minX:134,
minY:128,
maxX:202,
maxY:222,
prezzo:10,
carboidrati : 100,
grassi : 30,
proteine : 40,
}
];
To Re-Draw the same canvas over and over again, first you must reset canvas and then you can draw again.
See this snippet example:
// on document ready
document.addEventListener("DOMContentLoaded", function(){
function grafic(price, carbo, fat, protein){
try{
// Canvas Element
var canvas = document.getElementById("canvas")
var ctx = canvas.getContext("2d");
// Reset Canvas
ctx.setTransform(1, 0, 0, 1, 0, 0);
ctx.clearRect(0, 0, 500, 400);
// Draw (Re-Draw) Canvas
ctx.fillStyle='rgb(255,255,0)';
ctx.translate(0,200);
ctx.fillRect(10,-(price),40,150);
ctx.fillRect(100,-(carbo),40,150);
ctx.fillRect(200,-(fat),40,150);
ctx.fillRect(300,-(protein),40,150);
} catch(e){
alert("generaGrafico " + e);
}
}
// test buttons
document.getElementById("a").addEventListener("click",function() {
grafic(20, 40, 60, 80);
})
document.getElementById("b").addEventListener("click",function() {
grafic(40, 20, 60, 100);
})
document.getElementById("c").addEventListener("click",function() {
grafic(60, 40, 20, 10);
})
document.getElementById("d").addEventListener("click",function() {
grafic(80, 100, 40, 20);
})
});
div {
margin: 5px;
padding: 5px;
display: inline-block;
border: 1px solid black;
cursor: pointer;
}
<div id="a">Image A</div>
<div id="b">Image B</div>
<div id="c">Image C</div>
<div id="d">Image D</div>
<br/>
<canvas id="canvas" width="500" height="250"></canvas>
There is a typo "far" should be "fat"
ctx.fillRect(200,-(far),40,150);
I've slightly edited your code to demonstrate it works now.
function grafic(price, carbo, fat, protein){
try{
var canvas = document.getElementById("canvas")
var ctx = canvas.getContext("2d");
ctx.fillStyle='rgb(255,255,0)';
ctx.translate(0,200);
ctx.fillRect(10,-(price),40,150);
ctx.fillRect(100,-(carbo),40,150);
ctx.fillRect(200,-(fat),40,150);
ctx.fillRect(300,-(protein),40,150);
}catch(e){
alert("generaGrafico " + e);
}
}
grafic(4,4,4,4);
<canvas id="canvas" width="500" height="250" style="border:1px solid; height 20px; min-widht: 200px"></canvas>
Related
I want to display the content of a canvas as the background of another canvas and draw a bunch of rectangles on there. I need to dynamically change:
the canvas from which to load the background image for my finalcanvas
which rectangles to draw
It's easiest if I start this process from scratch. I have imitated starting from scratch with a simple button. However, after redrawing my canvas, previous information from fabric.js remains present after dragging an item of a canvas a bit. This means that the old canvas was not cleared properly. I tried playing around with .clear() and .depose(), but to no avail.
In case the description is vague, here an image:
And an small reproducible example:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Hello</title>
</head>
<body>
<canvas id="finalcanvas"></canvas>
<canvas id="backgroundcanvas" width="200" height="100"></canvas>
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/3.6.3/fabric.min.js" type="text/javascript"></script>
<script>
function load_plane_onto_active_canvas() {
var c = document.getElementById('backgroundcanvas');
var ctx = c.getContext("2d");
var bg = c.toDataURL("image/png");
var canvas = new fabric.Canvas('finalcanvas', {
width: 333,
height: 333
});
canvas.setBackgroundImage(bg, canvas.renderAll.bind(canvas));
canvas.on("mouse:over", function(e) {
console.log(e.target)
});
// add 100 rectangles
for (i = 0; i < 10; i++) {
for (j = 0; j < 10; j++) {
rect = new fabric.Rect({
width: 10,
height: 10,
left: j * 15,
top: i * 15,
fill: 'green',
})
canvas.add(rect);
}
}
}
window.onload = function() {
// fill the background canvas with red
(function() {
var canvas = document.getElementById("backgroundcanvas");
var ctx = canvas.getContext("2d");
ctx.fillStyle = "#FF0000";
ctx.fillRect(0, 0, 150, 75);
}())
load_plane_onto_active_canvas()
}
</script>
<button onclick="load_plane_onto_active_canvas()">click me</button>
</body>
</html>
I hope someone can help me!
Notice that you're creating a new instance of fabric.Canvas in your load_plane_onto_active_canvas() function. So when you're clicking the button, the existing canvases stay intact, and you're actually calling clear() on your freshly created canvas. The DOM becomes messed up at this point, with several nested canvases inside of each other - you can take a peek at the DOM inspector to see that.
What you can do instead is create the canvas instance just once, then work with a reference to it later in your other functions.
const finalCanvas = new fabric.Canvas("finalcanvas", {
width: 333,
height: 333
});
// finalCanvas now exists in the global (window) scope
function load_plane_onto_active_canvas() {
var c = document.getElementById("backgroundcanvas");
var ctx = c.getContext("2d");
var bg = c.toDataURL("image/png");
finalCanvas.clear();
finalCanvas.setBackgroundImage(bg, finalCanvas.renderAll.bind(finalCanvas));
finalCanvas.on("mouse:over", function (e) {
// console.log(e.target);
});
// add 100 rectangles
for (i = 0; i < 10; i++) {
for (j = 0; j < 10; j++) {
rect = new fabric.Rect({
width: 10,
height: 10,
left: j * 15,
top: i * 15,
fill: "green"
});
finalCanvas.add(rect);
}
}
}
window.onload = function () {
// fill the background canvas with red
(function () {
var canvas = document.getElementById("backgroundcanvas");
var ctx = canvas.getContext("2d");
ctx.fillStyle = "#FF0000";
ctx.fillRect(0, 0, 150, 75);
})();
load_plane_onto_active_canvas();
};
document.querySelector("#b1").onclick = () => load_plane_onto_active_canvas();
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/3.6.3/fabric.js"></script>
<canvas id="finalcanvas"></canvas>
<canvas id="backgroundcanvas" width="200" height="100"></canvas>
<button id="b1">start from scratch</button>
Whilst learning, i keep going back over what i have done to understand it properly, make changes and improvements. At the moment, i am going over my hide/show function.
I have 3 buttons which controls the visibility of their respective canvases.
My code did look like this:
function toggleRedDot(){
document.getElementById('btnRed').click(); {
if(canvas.style.visibility=='hidden'){
canvas.style.visibility='visible';
btnRed.style.color='Red';
}else{
canvas.style.visibility='hidden';
btnRed.style.color='Black';
}
}
};
Both the Green and Blue toggle functions are identical except for the button color. My project actually has 7 buttons and canvases. I'm trying to put all these functions into 1, and this is now what i have:
function toggle_visibility(id)
{
var ele = document.getElementById(id);
if (ele.style.visibility == 'hidden')
{
ele.style.visibility = 'visible';
}
else
{
ele.style.visibility = 'hidden';
}
}
I have been unable to work out how to now change the corresponding button color attribute. The idea is that the button will look 'on' when canvas is visible and the color reverts to Black when 'off'.
I also need the answer to be in just javascript please, as this is what i am learning.
I am also hoping to do a similar sort of thing to my drawDots function as they also differ in class, and location. Thank you.
I made a snippet:
var canvas = document.getElementById('redDot');
var ctxR = canvas.getContext('2d');
var canvas1 = document.getElementById('greenDot');
var ctxG = canvas1.getContext('2d');
var canvas2 = document.getElementById('blueDot');
var ctxB = canvas2.getContext('2d');
var cw = canvas.width;
var ch = canvas.height;
drawDots();
function toggle_visibility(id) {
var ele = document.getElementById(id);
if (ele.style.visibility == 'hidden') {
ele.style.visibility = 'visible';
} else {
ele.style.visibility = 'hidden';
}
}
function drawDots() {
// Red Dot
ctxR.clearRect(0, 0, cw, ch);
ctxR.save();
ctxR.translate(cw / 2, ch / 2);
ctxR.beginPath();
ctxR.arc(-50, 0, 10, 0, 2 * Math.PI);
ctxR.closePath();
ctxR.fillStyle = 'Red';
ctxR.fill();
ctxR.restore();
// Green Dot
ctxG.clearRect(0, 0, cw, ch);
ctxG.save();
ctxG.translate(cw / 2, ch / 2);
ctxG.beginPath();
ctxG.arc(0, 0, 10, 0, 2 * Math.PI);
ctxG.closePath();
ctxG.fillStyle = 'Green';
ctxG.fill();
ctxG.restore();
// Blue Dot
ctxB.clearRect(0, 0, cw, ch);
ctxB.save();
ctxB.translate(cw / 2, ch / 2);
ctxB.beginPath();
ctxB.arc(50, 0, 10, 0, 2 * Math.PI);
ctxB.closePath();
ctxB.fillStyle = 'Blue';
ctxB.fill();
ctxB.restore();
};
#wrapper {
position: relative;
}
canvas {
position: absolute;
border: 1px solid red;
}
.red {
color: Red;
}
.green {
color: Green;
}
.blue {
color: Blue;
}
<div id="btnWrapper">
<button id="btnRed" class="red" title="Toggle Red Dot" onclick="toggle_visibility('redDot')">☉</button>
<button id="btnGreen" class="green" title="Toggle Green Dot" onclick="toggle_visibility('greenDot')">☉</button>
<button id="btnBlue" class="blue" title="Toggle Blue Dot" onclick="toggle_visibility('blueDot')">☉</button>
</div>
<div id="wrapper">
<canvas id="redDot" width="200" height="100"></canvas>
<canvas id="greenDot" width="200" height="100"></canvas>
<canvas id="blueDot" width="200" height="100"></canvas>
</div>
There are a variety of ways to approach this. The way I chose simply passes the id of the button along with the id of the canvas to toggle_visibility. I also added a small object so that the color for a button can be looked up via a button id.
Things to consider in the future:
Because DOM reads/writes are somewhat expensive, consider reading the DOM once and storing the elements on the JS side
Consider using addEventListener from JS instead of onclick in HTML
Your markup will be cleaner
You won't have to pass parameters like btnRed and redDot because event listeners receive an event which knows which element fired the event
Instead of manipulating the visibility/color directly in the JS, have a CSS class (or classes) that do what you want and just change the class.
Good luck! :)
var canvas = document.getElementById('redDot');
var ctxR = canvas.getContext('2d');
var canvas1 = document.getElementById('greenDot');
var ctxG = canvas1.getContext('2d');
var canvas2 = document.getElementById('blueDot');
var ctxB = canvas2.getContext('2d');
var cw = canvas.width;
var ch = canvas.height;
drawDots();
var buttonIdToColor = {
btnRed: 'red',
btnGreen: 'green',
btnBlue: 'blue'
};
function toggle_visibility(buttonId, dotId) {
var button = document.getElementById(buttonId)
var dot = document.getElementById(dotId);
if (dot.style.visibility == 'hidden') {
dot.style.visibility = 'visible';
button.style.color = buttonIdToColor[buttonId];
} else {
dot.style.visibility = 'hidden';
button.style.color = 'black';
}
}
function drawDots() {
// Red Dot
ctxR.clearRect(0, 0, cw, ch);
ctxR.save();
ctxR.translate(cw / 2, ch / 2);
ctxR.beginPath();
ctxR.arc(-50, 0, 10, 0, 2 * Math.PI);
ctxR.closePath();
ctxR.fillStyle = 'Red';
ctxR.fill();
ctxR.restore();
// Green Dot
ctxG.clearRect(0, 0, cw, ch);
ctxG.save();
ctxG.translate(cw / 2, ch / 2);
ctxG.beginPath();
ctxG.arc(0, 0, 10, 0, 2 * Math.PI);
ctxG.closePath();
ctxG.fillStyle = 'Green';
ctxG.fill();
ctxG.restore();
// Blue Dot
ctxB.clearRect(0, 0, cw, ch);
ctxB.save();
ctxB.translate(cw / 2, ch / 2);
ctxB.beginPath();
ctxB.arc(50, 0, 10, 0, 2 * Math.PI);
ctxB.closePath();
ctxB.fillStyle = 'Blue';
ctxB.fill();
ctxB.restore();
};
#wrapper {
position: relative;
}
canvas {
position: absolute;
border: 1px solid red;
}
.red {
color: Red;
}
.green {
color: Green;
}
.blue {
color: Blue;
}
<div id="btnWrapper">
<button id="btnRed" class="red" title="Toggle Red Dot" onclick="toggle_visibility('btnRed', 'redDot')">☉</button>
<button id="btnGreen" class="green" title="Toggle Green Dot" onclick="toggle_visibility('btnGreen', 'greenDot')">☉</button>
<button id="btnBlue" class="blue" title="Toggle Blue Dot" onclick="toggle_visibility('btnBlue', 'blueDot')">☉</button>
</div>
<div id="wrapper">
<canvas id="redDot" width="200" height="100"></canvas>
<canvas id="greenDot" width="200" height="100"></canvas>
<canvas id="blueDot" width="200" height="100"></canvas>
</div>
As you say there are more than 3 colours I have written an example that will simplify the creation of multiply buttons.
The buttons and canvases are uniquely identified by their colour so I use the colours to query the DOM for references and use a function closure (forEach callback function) to hold the references for when needed.
I would have actually created the buttons and canvas inside that function as well but you may want it in the html so there will still be so tedious markup to create when adding more buttons.
Rather than manipulate the element styles I have add the DOM classes, .black for the button, and .hide and .show for the canvas. So all that is needed is to set the element's class name rather than their styles. Will make it easier if you have a complicated set of styles to switch between.
"Red,Green,Blue".split(",").forEach((col,i)=>{
var col_l = col.toLowerCase();
var ctx = document.getElementById(col_l+ 'Dot').getContext('2d');
var btn = document.getElementById("btn"+col);
btn.title ="Toggle "+col +" Dot" ;
btn.className = col_l ;
ctx.canvas.className = "show";
var cw = ctx.canvas.width;
var ch = ctx.canvas.height;
ctx.clearRect(0, 0, cw, ch);
ctx.fillStyle = col;
ctx.beginPath();
ctx.arc(cw/2 -50 + i * 50, ch/2, 10, 0, 2 * Math.PI);
ctx.fill();
function toggle(){ // this function has closure over col_l ,ctx, and btn
if (ctx.canvas.className == 'hide') {
ctx.canvas.className = "show";
btn.className = col_l ;
}else{
ctx.canvas.className = "hide";
btn.className = "black";
}
}
btn.addEventListener("click",toggle);
});
#wrapper {
position: relative;
}
canvas {
position: absolute;
border: 1px solid red;
}
/* for canvas hide and show */
.hide {
visibility : hidden;
}
.show {
visibility : visible;
}
.red {
color: Red;
}
.green {
color: Green;
}
.blue {
color: Blue;
}
.black { /* "black" colour button */
color: Black;
}
<!-- removed most of the stuff that is better added in code. -->
<div id="btnWrapper">
<button id="btnRed">☉</button>
<button id="btnGreen">☉</button>
<button id="btnBlue">☉</button>
</div>
<div id="wrapper">
<canvas id="redDot" width="200" height="100"></canvas>
<canvas id="greenDot" width="200" height="100"></canvas>
<canvas id="blueDot" width="200" height="100"></canvas>
</div>
I am trying to creat a minesweeper board in Javascript, but first i tried to creat only one cell. this is my code. It was suppose to draw a cell from the 30px width and 30px height picture that i created ( cell.png ) but when i run the code i only see the canvas. What am i doing wrong?
<!DOCTYPE html>
<html>
<head>
<script>
var s = {
rows: 10,
col: 10,
width: 30,
height: 30,
};
var c;
window.onload = function(){
var canvas = document.getElementById("myCanvas");
c = canvas.getContext("2d");
// c.fillRect(50,50,300,300);
init();
}
var box;
function init(){
box = new Image();
box.src = "cell.png";
draw();
}
function draw(){
c.clearRect(0,0,400,400);
c.drawImage(box,10,10);
}
</script>
</head>
<body>
<div id="controls">
</div>
<div id="game">
<canvas id="myCanvas" width="400" height="400" style="border:1px solid #c3c3c3;">
</canvas>
</div>
</body>
</html>
The problem you are experiencing is because your code is not waiting for the image to load before trying to draw it. You need to wait till it is loaded then call the drawing code, this can be done from the image's onload event
var s = {
rows: 10,
col: 10,
width: 30,
height: 30,
};
var c;
window.onload = function(){
var canvas = document.getElementById("myCanvas");
c = canvas.getContext("2d");
init();
}
var box;
function init(){
box = new Image();
//onload will be called once the image has loaded
box.onload = function(){
//Here you call draw.
draw();
};
box.src = "http://placehold.it/30x30"; //"cell.png";
}
function draw(){
c.clearRect(0,0,400,400);
c.drawImage(box,10,10);
}
<canvas width="200" height="200" id="myCanvas"></canvas>
I am fairly new to javascript and HTML5, so excuse me if it turns out to be a silly mistake
I am drawing 3 canvas programmatically with some text and a rectangle and i want a click event on each one of them, also I need to know which canvas has been clicked, i wrote the following code but doMouseDown function is executed even without click
<canvas id="myCanvas1" width="452" height="80" style="border:0px solid #d3d3d3;"></canvas>
<canvas id="myCanvas2" width="452" height="80" style="border:0px solid #d3d3d3;"></canvas>
<canvas id="myCanvas3" width="452" height="80" style="border:0px solid #d3d3d3;"></canvas>
<script>
function init()
{
var rect = { w: 300, h: 60 };
var point = { x: 150, y: 10 };
for (var i = 1 ; i < 4 ; i++) {
var canvasStr = "myCanvas" + Number(i);
var c = document.getElementById(canvasStr);
var context = c.getContext("2d");
// text
context.font = "22pt Arial";
context.lineWidth = 2;
context.fillStyle = "#000000";
var studentStr = "Student " + Number(i);
context.fillText(studentStr, 5, 50);
// rectangle
context.strokeStyle = "black";
context.strokeRect(point.x, point.y, rect.w, rect.h);
context.fillStyle = "#00FF00";
context.fillRect(point.x, point.y, rect.w, rect.h);
c.addEventListener('mousedown', doMouseDown(canvasStr), false);
}
}
function doMouseDown(canvasStr)
{
alert(canvasStr);
}
init();
</script>
How can i fix it and know which canvas has been clicked (canvasStr in this case)
You can do it this way
c.addEventListener("mousedown", function () {
doMouseDown(canvasStr)
}, false);
and then write in that function
function doMouseDown(canvasStr) {
alert(canvasStr);
}
if you're attaching listeners in a loop, you have to create a closure, otherwise canvasStr will allways be == myCanvas3:
(function(str) {
c.addEventListener('mousedown', doMouseDown.bind(str), false);
}(canvasStr));
Then your callback should be:
function doMouseDown(e, canvasStr) {
alert(canvasStr);
}
First argument e is the Event object which is always passed to callbacks.
ADDED
There is no point in doing this:
var canvasStr = "myCanvas" + Number(i);
i is already a number and you are adding it to a string. Just write:
var canvasStr = "myCanvas" + i;
Another way is to wrap all 3 canvases in a container div and listen for events on the container
http://jsfiddle.net/m1erickson/g7fsS/
<div id="container">
<canvas id="myCanvas1" width="45" height="80"></canvas>
<canvas id="myCanvas2" width="45" height="80"></canvas>
<canvas id="myCanvas3" width="45" height="80"></canvas>
</div>
document.getElementById("container").addEventListener("click",function(e){
console.log(e.target.id);
},false);
Simply put, I'm trying to toggle a button to make a line bold (or not). I read a few questions here similar to this problem, but the solutions haven't helped me. Here's my code:
<!DOCTYPE HTML>
<html>
<head>
<style>
body {
margin: 0px;
padding: 0px;
}
</style>
</head>
<body>
<div id="DrawLineDiv">
<canvas id="DrawLineCanvas" width="578" height="200"></canvas>
<script>
var canvas = document.getElementById('DrawLineCanvas');
var context = canvas.getContext('2d');
// Use beginPath() to declare that a new path is to be drawn
context.beginPath();
// Place the drawing cursor at the desired point
context.moveTo(100, 150);
// Determine where to stop drawing
context.lineTo(450,50);
//Draw the line
context.stroke();
</script>
</div>
<script>
var canvas = document.getElementById("DrawLineCanvas");
//var context = canvas.getContext('2d');
function toggleLineBold(button) {
var button;
if (button == "BoldNow") {
context.lineWidth = 15;
context.stroke();
document.getElementById("BoldLineButton").onclick = function(){
toggleLineBold('Regular');
};
} else {
context.lineWidth = 1;
context.stroke();
document.getElementById("BoldLineButton").onclick = function(){
toggleLineBold('BoldNow');
};
return;
};
};
</script>
<div id="BoldLineButton" style="height:50px; width:120px; border:2px solid #6495ed; background-color:#bcd2ee; border-radius:10px; margin-left: 5px; text-align:center" onclick="toggleLineBold('BoldNow')">
<br/>Toggle Bold Line<br/>
</div>
</body>
</html>
The line changes to bold, but triggers an error in the javascript at the line trying to change the onclick event. I know I've got something wrong, I'm just not sure what.
Thank's in advance for your assistance.
LIVE DEMO
HTML:
<canvas id="DrawLineCanvas" width="578" height="200"></canvas>
<button id="BoldLineButton">Line size: <b>1</b></button>
JS:
var doc = document,
canvas = doc.querySelector('#DrawLineCanvas'),
boldBtn = doc.querySelector('#BoldLineButton'),
ctx = canvas.getContext('2d'),
size = [1, 3, 5, 10, 15], // use only [1, 15] if you want
currSize = 0; // size[0] = 1 // Index pointer to get the value out of the
// size Array
function draw(){
canvas.width = canvas.width;
ctx.beginPath();
ctx.moveTo(100, 150);
ctx.lineTo(450,50);
ctx.lineWidth = size[currSize]; // Use currSize Array index
ctx.stroke();
}
draw();
function toggleLineBold() {
++currSize; // Increase size and
currSize %= size.length; // loop if needed.
boldBtn.getElementsByTagName('b')[0].innerHTML = size[currSize];
draw();
}
boldBtn.addEventListener("click", toggleLineBold);