How would I fillRect with an image? - javascript

Normally you can fill a rectangle in a canvas withctx.fillStyle = "whatever color here" and then ctx.fillRect(cords and length and width here). Is there a syntax where I can say ctx.fillRect(someImagePathHere, xOfTopLeft, yofTopLeft)
If not, how else can I achieve this?

The question is ambiguous as there are many ways to "fillRect with an image".
First off images in the browser are downloaded asynchronously so you need to wait for an image to load before you can use it. For canvas situations the most common way to get an image is to create a new Image and set an onload listener
const img = new Image();
img.onload = someFunctionToCallWhenTheImageHasLoaded
img.src = 'http://url/to/image';
Then the question is what do mean by "fillRect"
Using this 256x256 image
For example to draw the image at the size it was downloaded you can use drawImage with 3 arguments
ctx.drawImage(img, x, y);
const img = new Image();
img.onload = draw;
img.src = 'https://i.imgur.com/ZKMnXce.png';
function draw() {
const ctx = document.querySelector('canvas').getContext('2d');
ctx.drawImage(img, 0, 0);
}
canvas { border: 1px solid black; }
<canvas></canvas>
To draw the image at a different size you can use
ctx.drawImage(img, x, y, width, height);
const img = new Image();
img.onload = draw;
img.src = 'https://i.imgur.com/ZKMnXce.png';
function draw() {
const ctx = document.querySelector('canvas').getContext('2d');
const destX = 10;
const destY = 20;
const destWidth = 30;
const destHeight = 40;
ctx.drawImage(img, destX, destY, destWidth, destHeight);
}
canvas { border: 1px solid black; }
<canvas></canvas>
To draw part of the image you can use
// part of image to draw
const srcX = 10;
const srcY = 20;
const srcWidth = 130;
const srcHeight = 140;
// where to draw it
const dstX = 60;
const dstY = 70;
const dstWidth = 160;
const dstHeight = 40;
ctx.drawImage(img, srcX, srcY, srcWidth, srcHeight,
dstX, dstY, dstWidth, dstHeight);
const img = new Image();
img.onload = draw;
img.src = 'https://i.imgur.com/ZKMnXce.png';
function draw() {
const ctx = document.querySelector('canvas').getContext('2d');
// part of image to draw
const srcX = 10;
const srcY = 20;
const srcWidth = 130;
const srcHeight = 140;
// where to draw it
const dstX = 60;
const dstY = 70;
const dstWidth = 160;
const dstHeight = 40;
ctx.drawImage(img, srcX, srcY, srcWidth, srcHeight,
dstX, dstY, dstWidth, dstHeight);
}
canvas { border: 1px solid black; }
<canvas></canvas>
That said, "fillRect" being ambiguous maybe you wanted to use the image as a pattern in which case you need to make a pattern out of it using createPattern
const pattern = ctx.createPatttern(img, 'repeat');
For these let's use this 16x16 pixel image
You can then use the pattern as your fillStyle as in
ctx.fillStyle = pattern;
ctx.fillRect(10, 20, 30, 40);
const img = new Image();
img.onload = draw;
img.src = 'https://i.imgur.com/fqgm8uh.png';
function draw() {
const ctx = document.querySelector('canvas').getContext('2d');
const pattern = ctx.createPattern(img, 'repeat');
ctx.fillStyle = pattern;
ctx.fillRect(10, 20, 30, 40);
}
canvas { border: 1px solid black; }
<canvas></canvas>
Patterns are relative to the origin of the canvas which means if you just use ctx.fillRect (or any other fill) the pattern will match across fills.
ctx.fillRect(10, 20, 30, 40);
ctx.beginPath();
ctx.arc(50, 60, 25, 0, Math.PI * 2);
ctx.fill();
const img = new Image();
img.onload = draw;
img.src = 'https://i.imgur.com/fqgm8uh.png';
function draw() {
const ctx = document.querySelector('canvas').getContext('2d');
const pattern = ctx.createPattern(img, 'repeat');
ctx.fillStyle = pattern;
ctx.fillRect(10, 20, 30, 40);
ctx.beginPath();
ctx.arc(50, 60, 25, 0, Math.PI * 2);
ctx.fill();
}
canvas { border: 1px solid black; }
<canvas></canvas>
Because patterns are anchored at the origin if you are animating without changing the origin you'll notice the pattern doesn't move
const img = new Image();
img.onload = start;
img.src = 'https://i.imgur.com/fqgm8uh.png';
function start() {
const ctx = document.querySelector('canvas').getContext('2d');
const pattern = ctx.createPattern(img, 'repeat');
function render(time) {
time *= 0.001; // seconds;
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
const x = Math.sin(time * 1.1) * 150 + 150;
const y = Math.sin(time * 1.2) * 50 + 50;
ctx.fillStyle = pattern;
ctx.fillRect(x, y, 30, 40);
ctx.beginPath();
ctx.arc(x, y, 25, 0, Math.PI * 2);
ctx.fill();
requestAnimationFrame(render);
}
requestAnimationFrame(render);
}
canvas { border: 1px solid black; }
<canvas></canvas>
In order to move the pattern you need to move the origin of the canvas using ctx.translate (as well as ctx.rotate, ctx.scale, ctx.setTransform)
const img = new Image();
img.onload = start;
img.src = 'https://i.imgur.com/fqgm8uh.png';
function start() {
const ctx = document.querySelector('canvas').getContext('2d');
const pattern = ctx.createPattern(img, 'repeat');
function render(time) {
time *= 0.001; // seconds;
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
const x = Math.sin(time * 1.1) * 150 + 150;
const y = Math.sin(time * 1.2) * 50 + 50;
ctx.translate(x, y);
ctx.fillStyle = pattern;
ctx.fillRect(0, 0, 30, 40);
ctx.beginPath();
ctx.arc(0, 0, 25, 0, Math.PI * 2);
ctx.fill();
ctx.setTransform(1, 0, 0, 1, 0, 0); // set it back to the default
requestAnimationFrame(render);
}
requestAnimationFrame(render);
}
canvas { border: 1px solid black; }
<canvas></canvas>

Here's an illustration of some of the possibilities:
var im = new Image();
im.src = "https://upload.wikimedia.org/wikipedia/commons/7/79/Face-smile.svg";
im.onload = function () { /* first, wait until the image is loaded */
/* create five canvases, and draw something in each */
for (var i=1; i<=5; i++) {
var canvas = document.createElement("canvas");
document.body.appendChild(canvas);
canvas.width = canvas.height = 200;
var ctx=canvas.getContext("2d");
var x=50, y=50; /* where to plot */
var w=20, h=60; /* width and height of rectangle, if applicable */
switch (i) {
case 1:
/* first canvas: draw a rectangle */
ctx.fillRect(x, y, w, h);
break;
case 2:
/* second canvas: draw an image, actual size, no clipping */
/* coordinates are where the top left of the image is plotted */
ctx.drawImage(im, x, y);
break;
case 3:
/* third canvas: draw an image, scaled to rectangle */
ctx.drawImage(im, x, y, w, h);
break;
case 4:
/* fourth canvas: draw an image, actual size, clipped to rectangle */
ctx.save();
ctx.rect(x, y, w, h);
ctx.clip();
ctx.drawImage(im, x, y);
ctx.restore();
break;
case 5:
/* fifth canvas: draw shapes filled with a background image */
ctx.fillStyle = ctx.createPattern(im, 'repeat'); /* or 'no-repeat', or 'repeat-x', or 'repeat-y' */
/* note that the image is tiled from the top left of the canvas */
ctx.fillRect(x, y, w, h);
/* also draw a circle, why not */
ctx.beginPath();
ctx.arc(150, 150, 40, 0, Math.PI*2);
ctx.fill();
break;
}
}
}
im.onerror = function() { alert("failed to load image"); };
Jsfiddle: http://jsfiddle.net/efeqjjno/

Here is a quick example of how you can use drawImage to draw an image to a canvas. The element on the left is the image, the element on the right is the canvas with the image drawn on it.
JSFiddle: https://jsfiddle.net/gw8ncg7g/
window.onload = function() {
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
var img = document.getElementById("image");
ctx.drawImage(img, 0, 0);
}
canvas {
border:1px solid #d3d3d3;
}
<img id="image" width="300" height="300" src="http://i.imgur.com/LDR6AWn.png?1">
<canvas id="myCanvas" width="300" height="300" >

Related

Why do we have to rotate main canvas when trying rotate sprite image?

In every tutorial that I could find for how to rotate a sprite image on a canvas the canvas itself is rotated before applying sprite to it:
function drawSprite(sprite, x, y, deg)
{
const width = sprite.width / 2,
height = sprite.height / 2;
x = x + width;
y = y + height;
//clear main canvas
mainCtx.fillRect(0, 0, mainCanvas.width, mainCanvas.height);
// move origin to the coordinates of the center where sprite will be drawn
mainCtx.translate(x, y);
// rotate canvas
mainCtx.rotate(deg);
// draw sprite
mainCtx.drawImage(sprite, -width, -height);
// restore previous rotation and origin
mainCtx.rotate(-deg);
mainCtx.translate(-x, -y);
}
//never mind the rest
const mainCtx = mainCanvas.getContext("2d"),
sprite = (() =>
{
const canvas = document.createElement("canvas"),
ctx = canvas.getContext("2d"),
width = canvas.width = ctx.width = 100,
height = canvas.height = ctx.height = 50;
ctx.font = '20px arial';
ctx.textBaseline = "middle";
ctx.textAlign = "center";
ctx.fillStyle = "lightgreen";
ctx.fillRect(0, 0, width, height);
ctx.strokeRect(0, 0, width, height);
ctx.strokeText("my sprite", width/2, height/2);
return canvas;
})();
let r = 0;
const d = Math.sqrt(sprite.width *sprite.width + sprite.height*sprite.height),
w = mainCanvas.width = mainCtx.width = 400,
h = mainCanvas.height = mainCtx.height = 200;
mainCtx.fillStyle = "pink";
setInterval(() =>
{
const deg = r++*Math.PI/180;
let x = ((w-d)/2) + (Math.sin(deg)*((w-d)/2)),
y = ((h-d)/1.2) + (Math.cos(deg)*((h-d)/2));
drawSprite(sprite, x, y, deg);
}, 10);
<canvas id="mainCanvas"></canvas>
To me this is counterintuitive, why can't we rotate sprite itself before drawing it on main canvas? Why doesn't this work?
function drawSprite(sprite, x, y, deg)
{
const spriteCtx = sprite.getContext("2d");
//clear main canvas
mainCtx.fillRect(0, 0, mainCanvas.width, mainCanvas.height);
// rotate sprite
spriteCtx.rotate(deg);
// draw sprite
mainCtx.drawImage(sprite, x, y);
}
//never mind the rest
const mainCtx = mainCanvas.getContext("2d"),
sprite = (() =>
{
const canvas = document.createElement("canvas"),
ctx = canvas.getContext("2d"),
width = canvas.width = ctx.width = 100,
height = canvas.height = ctx.height = 50;
ctx.font = '20px arial';
ctx.textBaseline = "middle";
ctx.textAlign = "center";
ctx.fillStyle = "lightgreen";
ctx.fillRect(0, 0, width, height);
ctx.strokeRect(0, 0, width, height);
ctx.strokeText("my sprite", width/2, height/2);
return canvas;
})();
let r = 0;
const d = Math.sqrt(sprite.width *sprite.width + sprite.height*sprite.height),
w = mainCanvas.width = mainCtx.width = 400,
h = mainCanvas.height = mainCtx.height = 200;
mainCtx.fillStyle = "pink";
setInterval(() =>
{
const deg = r++*Math.PI/180;
let x = ((w-d)/2) + (Math.sin(deg)*((w-d)/2)),
y = ((h-d)/1.2) + (Math.cos(deg)*((h-d)/2));
drawSprite(sprite, x, y, deg);
}, 10);
<canvas id="mainCanvas"></canvas>
Wouldn't it be faster rotate a 100x100 sprite vs 10000x10000 main canvas?
Because the drawImage function takes only x(s),y(s) coordinate and width(s),/height(s).
I.e it only ever draws a straight rectangle, there is no way to make it draw anything skewed.
So you have to rotate the context's Current Transformation Matrix (CTM), which is not the canvas, so that the drawing is transformed.
Note that drawing a bitmap as a rectangle is a very basic model for drawing APIs.
As for the speed, once again you don't rotate the canvas, only the CTM and this only affects the future drawings and costs almost nothing anyway.
const canvas = document.querySelector("canvas");
const ctx = canvas.getContext("2d");
ctx.font = "30px sans-serif";
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.translate(150, 75);
const txtArr = [ "first", "second", "third", "fourth" ];
const colors = [ "red", "blue", "green", "orange" ];
for (let i = 0; i < txtArr.length; i++) {
ctx.fillStyle = colors[i];
ctx.fillText(txtArr[i], 0, 0);
// This doesn't rotate the previous drawings
ctx.rotate(Math.PI / txtArr.length);
}
<canvas></canvas>
So, yes, you could have your own drawImage(source, matrix) which would be something like
this.save();
this.setTransform(matrix);
this.drawImage(source, 0, 0);
this.restore();
But as you can see this means actually more operations per draw call, and thus performing two drawings on the same CTM would actually cost more than setting the CTM only once.

How to render custom Class objects in HTML canvas

Solved Had to render the images globally outside of the draw method. Here's a link to the github if you find this question and are wondering what the solution looks like in the full code. Its a bit too long to post here as an update. github canvas orbs
back to the original question:
I'm trying to render custom Class objects inside an HTML canvas. Doesn't work with class, but the the same data works without class.
Here's the code:
import './styles/index.css';
let canvas = document.getElementById("canvas");
let context = canvas.getContext("2d");
var window_height = window.innerHeight;
var window_width = window.innerWidth;
canvas.width = 500;
canvas.height = 400;
canvas.style.background = "#232a2e"
const convertSVG = (svgid) => {
const svg = document.getElementById(svgid);
const xml = new XMLSerializer().serializeToString(svg);
const svg64 = btoa(xml);
const b64Start = 'data:image/svg+xml;base64, ';
return b64Start + svg64;
}
class Orb {
constructor(xpos, ypos, radius, speed, image) {
this.xpos = xpos;
this.ypos = ypos;
this.radius = radius;
this.speed = speed;
this.image = convertSVG(image);
}
draw(context) {
const img = new Image();
img.onload = function() {
context.save();
context.beginPath();
context.arc(this.xpos, this.ypos, this.radius, 0, Math.PI * 2, false);
context.clip();
context.drawImage(img, (this.xpos - this.radius), (this.ypos - this.radius), 64, 64);
context.restore();
}
img.src = this.image;
}
}
const myOrb = new Orb(150, 150, 30, 1, 'javascript-icon');
myOrb.draw(context);
console.log(myOrb);
const img = new Image();
img.onload = function() {
context.save();
context.beginPath();
context.arc(300, 300, 30, 0, Math.PI * 2, false);
context.clip();
context.drawImage(img, (300-32), (300-32), 64, 64);
context.restore();
}
img.src = convertSVG('javascript-icon');
Currently it's only displaying the object I draw explicitly at the bottom of the code, and not the object of class Orb.
Here's a screen cap of the canvas:
current canvas render
EDIT: Additionally, I can generate a class object and draw the orb based on that. Like so:
const myOrb = new Orb(300, 300, 30, 1, 'javascript-icon');
const myOrb2 = new Orb(150, 150, 30, 1, 'java-icon');
const img = new Image();
img.onload = function() {
context.save();
context.beginPath();
context.arc(myOrb.xpos, myOrb.ypos, myOrb.radius, 0, Math.PI * 2, false);
context.clip();
context.drawImage(img, (myOrb.xpos-myOrb.radius-2), (myOrb.ypos-myOrb.radius-2), 64, 64);
context.restore();
}
img.src = myOrb.imageSRC;
console.log(myOrb);
console.log(myOrb2);
myOrb2.draw(context);
myOrb renders, but myOrb2 which is drawn with the method of the class is not rendered.
Here's an approach to take. Load the images globally and call draw when image is loaded.
let canvas = document.getElementById("canvas");
let context = canvas.getContext("2d");
var window_height = window.innerHeight;
var window_width = window.innerWidth;
canvas.width = 500;
canvas.height = 400;
canvas.style.background = "#232a2e"
const img1 = new Image();
img1.src = 'https://cdn.iconscout.com/icon/free/png-256/javascript-2752148-2284965.png';
const img2 = new Image();
img2.src = 'https://iconape.com/wp-content/png_logo_vector/cib-javascript.png'
class Orb {
constructor(xpos, ypos, radius, speed, image) {
this.xpos = xpos;
this.ypos = ypos;
this.radius = radius;
this.speed = speed;
this.image = image;
}
draw(context) {
context.save();
context.beginPath();
context.arc(this.xpos, this.ypos, this.radius, 0, Math.PI * 2, false);
context.clip();
context.drawImage(this.image, (this.xpos - this.radius), (this.ypos - this.radius), 64, 64);
context.restore();
}
}
const myOrb = new Orb(150, 150, 30, 1, img1);
const myOrb2 = new Orb(350, 150, 30, 1, img2);
window.onload = function() {
myOrb.draw(context);
myOrb2.draw(context);
}
<canvas id="canvas"></canvas>

Blur behind transparent box in JavaScript canvas

How can I achieve a blur behind a transparent box (fillStyle = 'rgba(255, 255, 255, 0.2)') in JavaScript canvas? Here's what I've got so far:
var canvas = document.getElementById('draw');
var c = canvas.getContext('2d');
function main() {
c.fillStyle = '#222';
c.fillRect(0, 0, canvas.width, canvas.height);
c.fillStyle = '#000';
c.fillRect(32, 32, 64, 64);
c.fillStyle = 'rgba(255, 255, 255, 0.2)';
c.filter = 'blur(5px)';
c.fillRect(16, 16, 128, 24);
}
But what happens, is instead of blurring the background behind the rectangle, is the rectangle itself is blurred, kind of obviously.
In the final script, I will probably use paths instead of rects.
Context2D filters will be applied only on your new drawings, so to also blur the background, you would actually have to redraw the part of the background you want to be blurred.
Fortunately, canvas can drawImage itself.
var blurredRect = {
x: 80,
y: 80,
height: 200,
width: 200,
spread: 10
};
var ctx = canvas.getContext('2d');
var img = new Image();
img.onload = draw;
img.src = 'https://upload.wikimedia.org/wikipedia/commons/5/55/John_William_Waterhouse_A_Mermaid.jpg';
function draw() {
canvas.width = img.width / 2;
canvas.height = img.height / 2;
// first pass draw everything
ctx.drawImage(img, 0,0, canvas.width, canvas.height);
// next drawings will be blurred
ctx.filter = 'blur('+ blurredRect.spread +'px)';
// draw the canvas over itself, cropping to our required rect
ctx.drawImage(canvas,
blurredRect.x, blurredRect.y, blurredRect.width, blurredRect.height,
blurredRect.x, blurredRect.y, blurredRect.width, blurredRect.height
);
// draw the coloring (white-ish) layer, without blur
ctx.filter = 'none'; // remove filter
ctx.fillStyle = 'rgba(255,255,255,0.2)';
ctx.fillRect(blurredRect.x, blurredRect.y, blurredRect.width, blurredRect.height);
}
<canvas id="canvas"></canvas>
But, canvas blur filter is a bit different than CSS one in that it will make the spreading stay inside the drawn area. This means that in our case, we have a 5px border around our rectangle that is less blurred than the center.
To workaround, we can take the whole thing in a different order and play with globalCompositeOperation property*:
var blurredRect = {
x: 80,
y: 80,
height: 200,
width: 200,
spread: 10
};
var ctx = canvas.getContext('2d');
var img = new Image();
img.onload = draw;
img.src = 'https://upload.wikimedia.org/wikipedia/commons/5/55/John_William_Waterhouse_A_Mermaid.jpg';
function draw() {
var spread = blurredRect.spread,
ratio = 0.5,
// make our blurred rect spreads
x = blurredRect.x - spread,
y = blurredRect.y - spread,
w = blurredRect.width + (spread * 2),
h = blurredRect.height + (spread * 2);
canvas.width = img.width * ratio;
canvas.height = img.height * ratio;
// this time we will first draw the blurred rect
ctx.filter = 'blur('+ spread +'px)';
// this time we draw from the img directly
ctx.drawImage(img,
x / ratio, y / ratio, w / ratio, h / ratio,
x, y, w, h
);
// now we will want to crop the resulting blurred image to the required one, so we get a clear-cut
ctx.filter = 'none'; // remove filter
// with this mode, previous drawings will be kept where new drawings are made
ctx.globalCompositeOperation = 'destination-in';
ctx.fillStyle = '#000'; // make it opaque
ctx.rect(blurredRect.x, blurredRect.y, blurredRect.width, blurredRect.height);
ctx.fill(); // clear-cut done
// reuse our rect to make the white-ish overlay
ctx.fillStyle = 'rgba(255,255,255,0.2)';
// reset gCO to its default
ctx.globalCompositeOperation = 'source-over';
ctx.fill();
// now we will draw behind the our blurred rect
ctx.globalCompositeOperation = 'destination-over';
ctx.drawImage(img, 0,0, canvas.width, canvas.height);
// reset to defaults
ctx.globalCompositeOperation = 'source-over';
}
<canvas id="canvas"></canvas>
But this approach requires that we keep access to the whole background as a drawable thing, in the example above that was just an image, but in real life, this might mean you'd have to do this operation on a second offscreen canvas.
var blurredRect = {
x: 80,
y: 80,
height: 200,
width: 200,
spread: 2
};
var ctx = canvas.getContext('2d');
// create an off-screen canvas
var bCanvas = canvas.cloneNode();
var bCtx = bCanvas.getContext('2d');
var img = new Image();
img.onload = draw;
img.src = 'https://upload.wikimedia.org/wikipedia/commons/5/55/John_William_Waterhouse_A_Mermaid.jpg';
function draw() {
var spread = blurredRect.spread;
canvas.width = bCanvas.width = img.width / 2;
canvas.height = bCanvas.height = img.height / 2;
// now we have a composed background
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
ctx.font = '40px Impact';
ctx.fillStyle = 'white';
ctx.fillText('..SO BLUR ME..', 120, 282);
// make our clear-cut on the offscreen canvas
bCtx.filter = 'blur(' + spread +'px)';
bCtx.drawImage(canvas,
blurredRect.x - spread, blurredRect.y - spread, blurredRect.width + spread * 2, blurredRect.height + spread * 2,
blurredRect.x - spread, blurredRect.y - spread, blurredRect.width + spread * 2, blurredRect.height + spread * 2
);
// clear-cut
bCtx.filter = 'none';
bCtx.globalCompositeOperation = 'destination-in';
bCtx.beginPath();
bCtx.rect(blurredRect.x, blurredRect.y, blurredRect.width, blurredRect.height);
bCtx.fillStyle = '#000';
bCtx.fill();
// white-ish layer
bCtx.globalCompositeOperation = 'source-over';
bCtx.fillStyle = 'rgba(255,255,255,0.2)';
bCtx.fillRect(blurredRect.x, blurredRect.y, blurredRect.width, blurredRect.height);
// now just redraw on the visible canvas
ctx.drawImage(bCanvas, 0,0);
}
<canvas id="canvas"></canvas>
*One may say that instead of an offscreen canvas and gCO we could have used ctx.clip(), but since you said it might a more complex Path than a rect, I will not advise to do so. Indeed, while it would require less code, and maybe use less memory, clipping is just bad with antialiasing, and since you are doing blurring, that will just look plain ugly.

canvas rotate img horizontally

I am working on animation optimisation and i wanted to try out canvas to see how it performs but i am not experienced well in canvas and i dont know how to prepare concept of this kind of animation.
this is the gif that shows how animation should rotate like:
this is my current code of js:
var cvs = document.getElementById('coin-spin'),
ctx = cvs.getContext('2d'),
w = cvs.width = 400,
h = cvs.height = 400,
cx = w / 2,
cy = h / 2,
a = 0;
var img = new Image();
var loop = function() {
// BG
ctx.fillStyle = '#ccc';
ctx.fillRect(0, 0, w, h);
// draw image
ctx.save();
ctx.translate(cx, cy);
ctx.rotate(Math.PI / 180 * a);
ctx.translate(-cx, -cy);
ctx.drawImage(img, cx - (img.width / 2), cy - (img.height / 2));
ctx.restore();
// axis
ctx.strokeStyle = '#000';
ctx.beginPath();
ctx.moveTo(cx, 0);
ctx.lineTo(cx, h);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(0, cy);
ctx.lineTo(w, cy);
ctx.stroke();
//mod angle
a++;
window.requestAnimationFrame(loop);
};
img.onload = function() {
loop();
};
img.src = 'https://image.ibb.co/gqkeXx/coin.png';
and the working demo on fiddle.
Could someone show how to add to the code so the image would rotate horizontally like on the gif?
EDIT ----
I added the spin, as it was also something to do, but still struggling on how to rotate it.
To get around the problem of rotating the object along two axes (faking one by mapping width to a sine wave), you can use an offscreen canvas to render the coin rotating around one axis, then render that canvas applying the second rotation ;
//make an offscreen canvas for rendering the coin rotating around one axis
var offscreenCanvas = document.createElement('canvas');
var cvs = document.getElementById('coin-spin'),
ctx = cvs.getContext('2d'),
w = cvs.width = 400,
h = cvs.height = 400,
cx = w / 2,
cy = h / 2,
a = 0;
var img = new Image();
var frameCount = 0;
var loop = function() {
frameCount++;
// BG
ctx.fillStyle = '#ccc';
ctx.fillRect(0, 0, w, h);
offscreenContext.fillStyle = '#ccc';
offscreenContext.fillRect(0, 0, w, h);
//determine how wide to render the offscreen canvas so we can fake
//rotation around the second axis
var imgRenderWidth = offscreenCanvas.width * Math.sin(frameCount/10.0)
//render the coin rotating around one axis to the offscreen canvas
offscreenContext.save();
offscreenContext.translate(img.width/2, img.height/2);
offscreenContext.rotate(Math.PI / 180 * a);
offscreenContext.translate((0-img.width)/2, (0-img.height)/2);
offscreenContext.drawImage(img, 0,0);
offscreenContext.restore();
// draw offscreen canvas to the screen with our precalculated width
ctx.save();
ctx.drawImage(offscreenCanvas, cx - (imgRenderWidth / 2), cy - (offscreenCanvas.height / 2), imgRenderWidth, offscreenCanvas.height);
ctx.restore();
// axis
ctx.strokeStyle = '#000';
ctx.beginPath();
ctx.moveTo(cx, 0);
ctx.lineTo(cx, h);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(0, cy);
ctx.lineTo(w, cy);
ctx.stroke();
//mod angle
a++;
window.requestAnimationFrame(loop);
};
//once the image has loaded, we know what size our offscreen canvas needs to be
img.onload = function() {
offscreenCanvas.width = img.width;
offscreenCanvas.height = img.height;
loop();
};
img.src = 'https://image.ibb.co/gqkeXx/coin.png';
//prepare the offscreen context so we can render to it later
var offscreenContext = offscreenCanvas.getContext('2d');
https://jsfiddle.net/ay3h5vuo/

How to fade out an item in canvas

I have a full screen canvas in my page. There are also some dive elements over this canvas. there is an circle element in the canvas that moves with the cursor everywhere in the page. However when the cursor arrives to the div element over the canvas, the circle shape stays in the last place it was on the canvas before arriving to the div.
DEMO: JSFIDDLE
Is ther any way that I can fade-out the circle shape when the cursor is over the div element and fade it in when it backs to the canvas?
Also is there any other effect rather than fading out? like making it small and then fade-out...
Here is the bit of code related to the circle:
function writeMessage(canvas, message, x, y) {
var context = canvas.getContext('2d');
context.clearRect(0, 0, canvas.width, canvas.height);
var pattern = context.createPattern(imageResized, 'no-repeat');//Use imageResized, not imageObj.
context.fillStyle = pattern;
context.fill();
context.fillStyle = 'black';
context.beginPath();
context.arc(x, y, 60, 0, 2 * Math.PI);
}
Well, you can always create your own fade function that gets called on mouseout (or mouseleave) event. Here's one I quickly built for you:
function fadeOut(canvas, message, x, y, amount) {
var context = canvas.getContext('2d');
context.clearRect(0, 0, canvas.width, canvas.height);
var pattern = context.createPattern(imageResized, 'no-repeat');
context.fillStyle = pattern;
context.fill();
context.font = '28pt Calibri';
context.fillStyle = 'black';
context.beginPath();
context.arc(x, y, amount, 0, 2 * Math.PI);
if (amount > 0) {
setTimeout(function(){
fadeOut(canvas, message, x, y, --amount);
}, 2);
}
else {
context.clearRect(0, 0, canvas.width, canvas.height);
}
}
See it in action here: http://jsfiddle.net/2p9dn8ed/42/
Or in the snippet:
var canvas = document.getElementById('myCanvas');
var context = canvas.getContext('2d');
var x = 0;
var y = 0;
var width = window.innerWidth;
var height = window.innerHeight;
var imageObj = new Image();
console.log(window.innerWidth + "----" + window.innerHeight);
//Create another canvas to darw a resized image to.
var imageResized = document.createElement('canvas');
imageResized.width = width;
imageResized.height = height;
//Wait for the original image to low to draw the resize.
imageObj.onload = function() {
//Find hoe mauch to scale the image up to cover.
var scaleX = width / imageObj.width;
var scaleY = height / imageObj.height;
var scaleMax = Math.max(scaleX, scaleY);
var ctx = imageResized.getContext('2d');
ctx.scale(scaleMax, scaleMax);
ctx.drawImage(imageObj, 0, 0);
};
imageObj.src = 'http://www.html5canvastutorials.com/demos/assets/darth-vader.jpg';
function writeMessage(canvas, message, x, y) {
var context = canvas.getContext('2d');
context.clearRect(0, 0, canvas.width, canvas.height);
var pattern = context.createPattern(imageResized, 'no-repeat');//Use imageResized, not imageObj.
context.fillStyle = pattern;
context.fill();
context.font = '28pt Calibri';
context.fillStyle = 'black';
//context.fillText(message, x, y);
context.beginPath();
context.arc(x, y, 60, 0, 2 * Math.PI);
//context.stroke();
//
}
function fadeOut(canvas, message, x, y, amount) {
var context = canvas.getContext('2d');
context.clearRect(0, 0, canvas.width, canvas.height);
var pattern = context.createPattern(imageResized, 'no-repeat');//Use imageResized, not imageObj.
context.fillStyle = pattern;
context.fill();
context.font = '28pt Calibri';
context.fillStyle = 'black';
//context.fillText(message, x, y);
context.beginPath();
context.arc(x, y, amount, 0, 2 * Math.PI);
//context.stroke();
//
if (amount > 0) {
setTimeout(function(){
fadeOut(canvas, message, x, y, --amount);
}, 2);
}
else {
context.clearRect(0, 0, canvas.width, canvas.height);
}
}
function getMousePos(canvas, evt) {
var rect = canvas.getBoundingClientRect();
return {
x: evt.clientX - rect.left,
y: evt.clientY - rect.top
};
}
canvas.addEventListener('mousemove', function (evt) {
var mousePos = getMousePos(canvas, evt);
var message = 'Mouse position: ' + mousePos.x + ',' + mousePos.y;
writeMessage(canvas, message, mousePos.x, mousePos.y);
}, false);
canvas.addEventListener('mouseout', function(evt){
var mousePos = getMousePos(canvas, evt);
var message = 'Mouse position: ' + mousePos.x + ',' + mousePos.y;
console.log(1);
fadeOut(canvas, message, mousePos.x, mousePos.y, 60);
});
// Get the canvas element form the page
var canvas = document.getElementById("myCanvas");
/* Rresize the canvas to occupy the full page,
by getting the widow width and height and setting it to canvas*/
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
anvas, img {
display:block;
margin:1em auto;
border:1px solid black;
}
canvas {
background:url('../img/spiral_galaxy-1920x1080.jpg');
background-size: cover;
position: absolute;
left: 0; top: 0;
width: 100%; height: 100%;
z-index:-1;
}
div{
width:200px;
height:200px;
background:rgba(0,0,0,0.5);
position: fixed;
top: 20%;
left: 20%;
}
<canvas id="myCanvas" width="578" height="400"></canvas>
<div><h1>TEXT</h1></div>

Categories