I am learning javascript and I came to a point where I created an image like this:
function placeThePawn(){
base_image = new Image();
base_image.src = 'pawns/pawn_red.png';
base_image.onload = function(){
ctx.drawImage(base_image, 50, 50 , 32 , 32);
}
}
[I searched and read few tutorial to learn how to move the pawn]
Now I made a function to move the pawn (image) like this:
function moveThePawn() {
base_image.style.left = parseInt(base_image.style.left) + 100 + 'px';
}
Somehow it doesn't seem to work.
I want to know how can I implement this (move the pawn) and would also want to know why this is not working!
You will have to call the function movethePawn();
Have this in your <body onkeyup="moveSelection(event)">
and this in your script
function moveSelection(event) {
if(event.keyCode === 37) {
movethePawn();
}
}
EDIT 1 :
You may need to construct this image as an element in your DOM and have that element with document.getElementbyId and move it left. It won't work directly with an Image object
This code creates your pawn, and moves it after two seconds using only your functions (and a clearRect to remove the old icon)
var base_image;
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
function placeThePawn(){
base_image = new Image();
base_image.src = 'http://placehold.it/200x200';
base_image.onload = function(){
ctx.drawImage(base_image, 50, 50 , 32 , 32);
}
}
function moveThePawn() {
ctx.clearRect(0,0,200,100);
ctx.drawImage(base_image, 150, 50 , 32 , 32);
}
placeThePawn();
setTimeout(moveThePawn, 2000);
<canvas id="myCanvas" width="200" height="100" style="border:1px solid #000000;">
</canvas>
This is a better example of how to do this:
var base_image;
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
var clearMyCanvas = function(){ctx.clearRect(0,0,1000,1000)};
var pawn = {
x:50,
y:50,
h:32,
w:32,
render: function placeThePawn(){
base_image = new Image();
base_image.src = 'http://placehold.it/200x200';
base_image.onload = function(){
ctx.drawImage(base_image, pawn.x, pawn.y , pawn.w , pawn.h);
}
}
}
pawn.render();
// redraw all the things you want on your next screen here.
setTimeout(function(){
// clear canvas
clearMyCanvas();
// perform logics
pawn.x += 100;
// redraw
pawn.render()
}, 2000);
<canvas id="myCanvas" width="200" height="100" style="border:1px solid #000000;">
</canvas>
I think you are mixing up the html5 canvas element (as seen in ctx.drawImage(base_image, 50, 50 , 32 , 32);) with simple html and css.
this line: base_image.style.left = parseInt(base_image.style.left) + 100 + 'px'; is attempting to change the css style property of an element saved in a variable called base_image. But there is no such element.
What you need to change is not the style of base_image, but the canvas context ctx where base_image has been drawn.
function moveThePawn() {
ctx.drawImage(base_image, 50, 50 , 32 , (32+100));//add the 100 pixels to the canvas rendering of the image
}
Related
In general we can convert the HTML elements to string and then we can insert it into DOM later when needed. Similarly, I want to convert the "CANVAS" element to string along with its context properties.
In the following example, I am getting the string value of span tag with outerHTML property. Likewise I want to get the "CANVAS"element along with context properties.
Is there any method or property for this support?
Example code snippets:
var sp=document.createElement("span");
sp.innerHTML = "E2"
var e2 = sp.outerHTML;
$("#test1").append(e2);
var c=document.createElement("CANVAS");
var ctx=c.getContext("2d");
ctx.beginPath();
ctx.moveTo(20,20);
ctx.lineTo(100,20);
ctx.arcTo(150,20,150,70,50);
ctx.lineTo(150,120);
ctx.stroke();
var cn = c.outerHTML;
$("#test2").append(cn);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="test1">
<span>E1</span>
</div>
<div id="test2">
</div>
Seems like you already know how to get dom properties of the canvas object.
Now you only need "context" infos (image data as I understand it)
You can get the image data as a base64 string like this:
function CreateDrawing(canvasId) {
let canvas = document.getElementById(canvasId);
let ctx = canvas.getContext('2d');
ctx.beginPath();
ctx.moveTo(20,20);
ctx.lineTo(100,20);
ctx.arcTo(150,20,150,70,50);
ctx.lineTo(150,120);
ctx.stroke();
}
function GetDrawingAsString(canvasId) {
let canvas = document.getElementById(canvasId);
let pngUrl = canvas.toDataURL(); // PNG is the default
// or as jpeg for eg
// var jpegUrl = canvas.toDataURL("image/jpeg");
return pngUrl;
}
function ReuseCanvasString(canvasId, url) {
let img = new Image();
img.onload = () => {
// Note: here img.naturalHeight & img.naturalWidth will be your original canvas size
let canvas = document.getElementById(canvasId);
let ctx = canvas.getContext('2d');
ctx.drawImage(img, 0, 0);
}
img.src = url;
}
// Create something
CreateDrawing("mycanvas");
// save the image data somewhere
var url = GetDrawingAsString("mycanvas");
// re use it later
ReuseCanvasString("replicate", url);
<canvas id="mycanvas"></canvas>
<canvas id="replicate"></canvas>
In short no!
You should realize the difference between a standard DOM-element and a canvas-element:
A created DOM-element is part of the mark-up language that can be viewed and changed.
In the canvas a vector image is drawn based upon the rules created in script. These rules are not stored in the element as text but as the image and can't be subtracted from the canvas element.
However there are other possibilities. We can get the variables from the ctx-object. But no info about coordinates:
var c=document.createElement("CANVAS");
var ctx=c.getContext("2d");
ctx.beginPath();
ctx.moveTo(20,20);
ctx.lineTo(100,20);
ctx.arcTo(150,20,150,70,50);
ctx.lineTo(150,120);
ctx.stroke();
var ctxInfo = [];
for (ctxKey in ctx)
{
if (Object.prototype.toString.call(ctx[ctxKey]) !== "[object Function]" )
{
ctxInfo.push(ctxKey + " : " + ctx[ctxKey]);
}
}
console.log(ctxInfo);
To transfer from one canvas to the other I would keep a list (array or object) of instructions and write a generic function that applies them.
canvasObject = [["beginPath"], ["moveTo", 20, 20], ["lineTo", 100, 20], ["arcTo", 150, 20, 150, 70, 50], ["lineTo", 150, 120], ["stroke"]];
function createCanvas(cnvsObj)
{
var c = document.createElement("canvas");
var ctx = c.getContext("2d");
cnvsObj.forEach(function(element){
//loop through instructions
ctx[element[0]].apply(ctx, element.slice(1));
});
return c;
}
var a = createCanvas(canvasObject);
document.body.appendChild(a);
I'm learning to draw an image with canvas and getting a problem inside this example:
let img = new Image();
img.src = 'https://image.freepik.com/free-photo/hrc-siberian-tiger-2-jpg_21253111.jpg';
let a = function () {
let c1 = document.getElementById('c1'),
c2 = document.getElementById('c2');
c1.width = c2.width = 150;
c1.height = c2.width = 150;
c1.getContext('2d').drawImage(img, 0, 0, 150, 150);
c2.getContext('2d').drawImage(img, 0, 0, 150, 150);
};
let b = function () {
let c2 = document.getElementById('c2');
c2.width = 100;
c2.height = 100;
c2.getContext('2d').drawImage(c2, 0, 0, 100, 100);
};
let c = function () {
let c1 = document.getElementById('c1'),
c3 = document.getElementById('c3');
c3.width = 100;
c3.height = 100;
c3.getContext('2d').drawImage(c1, 0, 0, 100, 100);
};
a();
b();
c();
<div>
<canvas id="c1"></canvas>
</div>
<div>
<canvas id="c2"></canvas>
</div>
<div>
<canvas id="c3"></canvas>
</div>
Inside b function. I want to re-draw (resize) its own image with another size (changing width and height from 150 to 100). But it looks like it couldn't.
Then, I've tried to make another function (c). In this function, I've used the image of canvas c1 to re-draw image of canvas c3. That's ok.
So, my question is: Cannot canvas use its own image to draw an image for itself? (or maybe I've done something wrong)
Edit: At first I thought that using an HTMLCanvasElement in the drawImage() call was an incorrect argument type. That was wrong, it's a valid argument. The actual issue was that the code was not waiting for the image to load.
You need to take a look at how you are getting your initial image data for your first canvas. I would not expect it to work because you could be drawing the image data from img before it is actually loaded. You need to attach callbacks to img to wait for it to finish loading the image and then draw that image on a canvas.
Consider my example below that includes an asynchronous way to load an image, how to draw one canvas in another, and how to draw text (just to show differences between canvases).
function loadImage(path) {
return new Promise((resolve, reject) => {
let img = new Image();
img.addEventListener("load", () => {
resolve(img);
});
img.addEventListener("error", (err) => {
reject(err);
});
img.src = path;
});
}
loadImage("https://cdn.sstatic.net/Sites/stackoverflow/img/sprites.svg")
.then((img) => {
let c1 = document.getElementById("c1"),
c2 = document.getElementById("c2"),
c1Ctx = c1.getContext("2d"),
c2Ctx = c2.getContext("2d");
c1Ctx.drawImage(img, 0, 0, 150, 150);
c1Ctx.strokeText("I'm drawing on canvas 1", 25, 25);
c2Ctx.drawImage(c1, 25, 25, 100, 100);
c2Ctx.strokeText("I'm drawing on canvas 2", 25, 25);
})
.catch(console.error);
<canvas id="c1" width="150" height="150"></canvas>
<canvas id="c2" width="150" height="150"></canvas>
Another thing that you will likely run into since you want to be able to pull data out of your canvas is CORS issues. I point this out explicitly because the image that you are trying to draw onto your canvas is from a different domain (image.freepik.com in your example). Whenever you draw image data from another domain onto a canvas, that canvas becomes tainted an you can no longer use canvas' toBlob(), toDataURL(), or getImageData() methods. See: MDN: CORS enabled image.
I am trying to make a rectangle gradually change its colour (fade) from yellow to white using Javascript after a button is pressed. Sadly my code does not work. Could you help me figure out what is wrong with the code and how to fix it?
I have just begun studying Javascript. Sorry, if this question is stupid. Thank you in advance.
This is my code:
<!DOCTYPE html>
<html>
<body>
<canvas id="myCanvas" width="300" height="150" style="border:1px solid #d3d3d3;">
Your browser does not support the HTML5 canvas tag.</canvas> // create canvas to work with
<button onclick="fade(ctx)">Change the colour!</button>
<script>
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d"); //set context
ctx.rect(20, 20, 150, 100); // draw a rectangle
ctx.stroke(); // with border
ctx.fillStyle="#FFFF00"; // fill with yellow
ctx.fillRect(20,20,150,100);
function fade(ctx) { // fade function responsible for changing colour
var dom = getElementById(ctx), level = 1; // new object based on rectangle object, initial iterator is set to 1
function step() { // inner step function
var h = level.toString(16);
dom.fillStyle = '#FFFF' + h + h; // construct a new colour using h variable
if (level < 15) {
level += 1;
setTimeout(step, 100); // do this after every 100 ms
}
}
setTimeout(step, 100);
}
</script>
</body>
</html>
You almost have it with your code though. You are passing the context into the function already so no need for the getElementById(ctx) bit. In fact that will give you an error so remove that line from your code. Instead directly set the fillStyle on the ctx variable like this:
ctx.fillStyle = '#FFFF' + h + h;
and you also need to redraw the rectangle, so add this line:
ctx.fillRect(20,20,150,100);
after you set the colour. And that will do it.
I have a canvas that is drawing an image and clipping to create the effect that the image is being revealed. I have the code working properly I have tried using a debouce method and also rAF to increase the canvas rendering performance but I only saw small gains if any.
I suspect the way I am iterating through my array of x and y coordinates could be the issue.
It seems to lag quite a bit when it is out putting the array in console about the same rate as the circle appear on the screen.
Here is the redraw function:
function redraw(mouse) {
m.push(mouse);
m.forEach(function (a) {
ctx.drawImage(img, 0, 0);
ctx.beginPath();
ctx.rect(0, 0, 500, 500);
ctx.arc(a.x, a.y, 70, 0, Math.PI * 2, true);
ctx.clip();
ctx.fillRect(0, 0, 500, 500)
})
}
I guess what I am looking for is some advice to speed up my code so the rendering of the circles seems more like drawing.
Here is the working demo -> http://jsfiddle.net/naeluh/4h7GR/
There are several issues here :
• Your mouse code is a nightmare, traversing the DOM on every move.
• You are redrawing everything on each move.
So i suggest a way more efficient solution :
• stack two canvases, the one below is your image, the one on top is the mask.
• Deal efficiently with the mouse.
• Only clear part of the mask canvas on mouse move : just one circle drawn on the mask canvas for each move.
(for that i used a globalCompositeOperation = 'destination-out' )
Result is perfectly smooth either on Firefox, Chrome, or Safari .
(tested on mac OS).
the fiddle :
(you have to click to clear)
http://jsfiddle.net/gamealchemist/4h7GR/22/
html
<canvas style='position: absolute; top: 0;left: 0;' id="canvas1" width="500" height="500"></canvas>
<canvas style='position: absolute;top: 0;left: 0;' id="canvas2" width="500" height="500"></canvas>
js
var can = document.getElementById("canvas1");
var ctx = can.getContext("2d");
var can2 = document.getElementById("canvas2");
var ctx2 = can2.getContext("2d");
var img = new Image();
img.onload = function () { ctx.drawImage(img,0,0); };
img.src = "http://placekitten.com/500/500";
ctx2.fillStyle='#000';
ctx2.fillRect(0,0,500,500);
ctx2.globalCompositeOperation = 'destination-out';
function clearThis(x,y) {
console.log('toto');
ctx2.fillStyle='#F00000';
ctx2.beginPath();
ctx2.arc(x, y, 70, 0, Math.PI * 2, true);
ctx2.fill();
}
var mouse = {
x: 0,
y: 0,
down: false
};
function setupMouse(canvas, onMouseMove, preventDefault) {
var rectLeft, rectTop;
var hook = canvas.addEventListener.bind(canvas);
var mouseDown = updateMouseStatus.bind(null, true);
var mouseUp = updateMouseStatus.bind(null, false);
hook('mousedown', mouseDown);
hook('mouseup', mouseUp);
hook('mousemove', updateCoordinates);
hook('scroll', updateRect);
// var mouseOut = function() { mouse.down=false ; } ;
// hook('mouseout', mouseOut);
function updateMouseStatus(b, e) {
mouse.down = b;
updateCoordinates(e);
if (preventDefault) {
e.stopPropagation();
e.preventDefault();
}
}
function updateCoordinates(e) {
mouse.x = (e.clientX - rectLeft);
mouse.y = (e.clientY - rectTop);
onMouseMove(mouse.x, mouse.y);
}
function updateRect() {
var rect = canvas.getBoundingClientRect();
rectLeft = rect.left;
rectTop = rect.top;
}
updateRect();
};
setupMouse(can2, clearThis, true);
The Above Code will do Fine .. But nEed some Editing
I have Edited the Code in Fiddle ..and i beleive there Is some Improvement in perforamnce
So I looked a little more and found a bug as expected.
The main problem is the accumulation of the drawing path.
Why Need to add clip and fillRect at every go ..Do it at last... the Major issue solved,Like
can.addEventListener("mousemove", function (e) {
var mouse = getMouse(e, can);
requestAnimationFrame(function () {
redraw(mouse);
ctx.clip();
ctx.fillRect(0, 0, 500, 500);
console.log(mouse);
});
}, false);
2.The Updated JSFiidle is
UpdatedFiddle
What I'm trying to do is, if I hover over one of my drawn objects, I want to display a blurb saying which city it is, the population and an image of their downtown etc. as of right now i just trying yo get it to work. i also wondering if there something else i can you instead of alert, something that would make a bubble instead
<script>
function startCanvas() {
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
//first circle
var one = c.getContext("2d");
//second circle
var two = c.getContext("2d");
//third cirle
var three = c.getContext("2d");
//fourth circle
var four = c.getContext("2d");
//fifth cirle
var five = c.getContext("2d");
// new image
var image = new Image();
image.onload = function () {
ctx.drawImage(image, 69, 50);
//draw a circle
one.beginPath();
one.arc(180, 90, 10, 0, Math.PI * 2, true);
one.closePath();
one.fill();
two.beginPath();
two.arc(155, 138, 10, 0, Math.PI * 2, true);
two.closePath();
two.fill();
three.beginPath();
three.arc(160, 180, 10, 0, Math.PI * 2, true);
three.closePath();
three.fill();
four.beginPath();
four.arc(257, 210, 10, 0, Math.PI * 2, true);
four.closePath();
four.fill();
five.beginPath();
five.arc(238, 235, 10, 0, Math.PI * 2, true);
five.closePath();
five.fill();
};
image.src = 'denmark.jpg';
//function hover over circle one, give alert
one.addEventListener('mouseover',
function (e) {
e = e || window.event;
alert('this is a test');
}
);
}
</script>
</head>
<body onload="startCanvas()">
<canvas id="myCanvas" width="600" height="600";">
Your browser does not support the HTML5 canvas tag.
</canvas>
</body>
</html>
Your code will throw this error
Object # < CanvasRenderingContext2D > has no method 'addEventListener'
So Modify this part
//function hover over circle one, give alert
one.addEventListener('mouseover',
function (e) {
e = e || window.event;
alert('this is a test');
}
);
by this code
//function hover over circle one, give alert
c.addEventListener('mouseover',
function (e) {
e = e || window.event;
alert('this is a test');
}
,false);
This Will Work;
Have you considered using a slightly higher-level drawing library, such as RaphaelJS? Or EaselJS?
I've messed around with both of these, and they make this kind of interactive canvas work much more elegant, and will also improve your cross-browser compatibility.