Adjust canvas image size like 'background-size: cover' and responsive - javascript

What I want to do
Set background-size:cover-like effect on a canvas image
Re-draw a canvas image as a window is resized (responsive)
What I tried
I tried 2 ways below. Neither works.
Simulation background-size: cover in canvas
How to set background size cover on a canvas
Issue
The image's aspect ratio is not fixed.
I am not sure but the image does not seem to be re-rendered as a window is resized.
My code
function draw() {
// Get <canvas>
const canvas = document.querySelector('#canvas');
// Canvas
const ctx = canvas.getContext('2d');
const cw = canvas.width;
const ch = canvas.height;
// Image
const img = new Image();
img.src = 'https://source.unsplash.com/WLUHO9A_xik/1600x900';
img.onload = function() {
const iw = img.width;
const ih = img.height;
// 'background-size:cover'-like effect
const aspectHeight = ih * (cw / iw);
const heightOffset = ((aspectHeight - ch) / 2) * -1;
ctx.drawImage(img, 0, heightOffset, cw, aspectHeight);
};
}
window.addEventListener('load', draw);
window.addEventListener('resize', draw);
canvas {
display: block;
/* canvas width depends on parent/window width */
width: 90%;
height: 300px;
margin: 0 auto;
border: 1px solid #ddd;
}
<canvas id="canvas"></canvas>

The canvas width / height attributes do not reflect it's actual size in relation to it being sized with CSS. Given your code, cw/ch are fixed at 300/150. So the whole calculation is based on incorrect values.
You need to use values that actually reflect it's visible size. Like clientWidth / clientHeight.
A very simple solution is to update the canvas width/height attributes before using them for any calculations. E.g.:
canvas.width = canvas.clientWidth;
canvas.height = canvas.clientHeight;
Full example:
const canvas = document.querySelector('#canvas');
const ctx = canvas.getContext('2d');
// only load the image once
const img = new Promise(r => {
const img = new Image();
img.src = 'https://source.unsplash.com/WLUHO9A_xik/1600x900';
img.onload = () => r(img);
});
const draw = async () => {
// resize the canvas to match it's visible size
canvas.width = canvas.clientWidth;
canvas.height = canvas.clientHeight;
const loaded = await img;
const iw = loaded.width;
const ih = loaded.height;
const cw = canvas.width;
const ch = canvas.height;
const f = Math.max(cw/iw, ch/ih);
ctx.setTransform(
/* scale x */ f,
/* skew x */ 0,
/* skew y */ 0,
/* scale y */ f,
/* translate x */ (cw - f * iw) / 2,
/* translate y */ (ch - f * ih) / 2,
);
ctx.drawImage(loaded, 0, 0);
};
window.addEventListener('load', draw);
window.addEventListener('resize', draw);
.--dimensions {
width: 90%;
height: 300px;
}
.--border {
border: 3px solid #333;
}
canvas {
display: block;
margin: 0 auto;
}
#test {
background: url(https://source.unsplash.com/WLUHO9A_xik/1600x900) no-repeat center center;
background-size: cover;
margin: 1rem auto 0;
}
<canvas id="canvas" class="--dimensions --border"></canvas>
<div id="test" class="--dimensions --border"></div>

First, load only once your image, currently you are reloading it every time the page is resized.
Then, your variables cw and ch will always be 300 and 150 since you don't set the size of your canvas. Remember, the canvas has two different sizes, its layout one (controlled by CSS) and its buffer one (controlled by its .width and .height attributes).
You can retrieve the layout values through the element's .offsetWidth and .offsetHeight properties.
Finally, your code does a contain image-sizing. To do a cover, you can refer to the answers you linked to, and particularly to K3N's one
{
// Get <canvas>
const canvas = document.querySelector('#canvas');
// Canvas
const ctx = canvas.getContext('2d');
// Image
const img = new Image();
img.src = 'https://source.unsplash.com/WLUHO9A_xik/1600x900';
function draw() {
// get the correct dimension as calculated by CSS
// and set the canvas' buffer to this dimension
const cw = canvas.width = canvas.offsetWidth;
const ch = canvas.height = canvas.offsetHeight;
if( !inp.checked ) {
drawImageProp(ctx, img, 0, 0, cw, ch, 0, 0);
}
}
img.onload = () => {
window.addEventListener('resize', draw);
draw();
};
inp.oninput = draw;
}
// by Ken Fyrstenberg https://stackoverflow.com/a/21961894/3702797
function drawImageProp(ctx, img, x, y, w, h, offsetX, offsetY) {
if (arguments.length === 2) {
x = y = 0;
w = ctx.canvas.width;
h = ctx.canvas.height;
}
// default offset is center
offsetX = typeof offsetX === "number" ? offsetX : 0.5;
offsetY = typeof offsetY === "number" ? offsetY : 0.5;
// keep bounds [0.0, 1.0]
if (offsetX < 0) offsetX = 0;
if (offsetY < 0) offsetY = 0;
if (offsetX > 1) offsetX = 1;
if (offsetY > 1) offsetY = 1;
var iw = img.width,
ih = img.height,
r = Math.min(w / iw, h / ih),
nw = iw * r, // new prop. width
nh = ih * r, // new prop. height
cx, cy, cw, ch, ar = 1;
// decide which gap to fill
if (nw < w) ar = w / nw;
if (Math.abs(ar - 1) < 1e-14 && nh < h) ar = h / nh; // updated
nw *= ar;
nh *= ar;
// calc source rectangle
cw = iw / (nw / w);
ch = ih / (nh / h);
cx = (iw - cw) * offsetX;
cy = (ih - ch) * offsetY;
// make sure source rectangle is valid
if (cx < 0) cx = 0;
if (cy < 0) cy = 0;
if (cw > iw) cw = iw;
if (ch > ih) ch = ih;
// fill image in dest. rectangle
ctx.drawImage(img, cx, cy, cw, ch, x, y, w, h);
}
canvas {
display: block;
/* canvas width depends on parent/window width */
width: 90%;
height: 300px;
margin: 0 auto;
border: 1px solid #ddd;
background-image: url('https://source.unsplash.com/WLUHO9A_xik/1600x900');
background-size: cover;
background-repeat: no-repeat;
}
<label><input id="inp" type="checkbox">show CSS rendering</label>
<canvas id="canvas"></canvas>

Related

Canvas fill viewport and keep image ratio

I am trying to make the canvas area and image fill the viewport without the image stretching and retain its aspect ratio when you resize the page. Below is my current code.
var ctx = $("#demo")[0].getContext("2d"),
img = new Image(),
radius = 35,
blurryImageSrc = "https://s9.postimg.cc/u9nsmzlwf/image.jpg";
img.src = blurryImageSrc;
$(img).on("load", function() {
resizeCanvas();
$("#demo").on("mousemove", function(e) {
erase(getXY(e));
});
$("#reset").on("click", function() {
ctx.globalCompositeOperation = "source-over";
ctx.drawImage(img, 0, 0);
ctx.globalCompositeOperation = "destination-out";
});
ctx.drawImage(img, 0, 0, window.innerWidth, window.innerHeight);
ctx.globalCompositeOperation = "destination-out";
});
function getXY(e) {
var r = $("#demo")[0].getBoundingClientRect();
return { x: e.clientX - r.left, y: e.clientY - r.top };
}
function erase(pos) {
ctx.beginPath();
ctx.arc(pos.x, pos.y, radius, 0, 2 * Math.PI);
ctx.closePath();
ctx.fill();
}
var can = document.getElementById('demo');
function resizeCanvas() {
can.style.width = window.innerWidth + 'px';
setTimeout(function() {
can.style.height = window.innerHeight + 'px';
}, 0);
}
window.onresize = resizeCanvas;
resizeCanvas();
https://jsfiddle.net/65fph0mn/8/
To retain the image's correct proportions, you need to query it's 'natural' width and height. The image object itself has two properties for this purpose: naturalWidth and naturalHeight.
As the image finished loading you must decide by which factor to scale the image based on the dimensions of the browser window and the longer side of your image.
Let's have a look at a simple example. Say your browser window's width is 1200 pixel and the image 250. If we now divide 1200 by 250 we get 4.8 - that's the factor we need to multiply the image's width and height to fill the current browser window in one direction while maintaining it's correct aspect ratio.
Here's an example:
var ctx = $("#demo")[0].getContext("2d"),
img = new Image(),
radius = 35,
blurryImageSrc = "https://s9.postimg.cc/u9nsmzlwf/image.jpg";
/// setup logic
img.src = blurryImageSrc;
$(img).on("load", function() {
resizeCanvas();
});
var can = document.getElementById('demo');
function resizeCanvas() {
can.width = window.innerWidth;
can.height = window.innerHeight;
if (img.width > 0) {
let factor = can.width / img.naturalWidth * img.naturalHeight > window.innerHeight ? can.height / img.naturalHeight : can.width / img.naturalWidth;
ctx.drawImage(img, 0, 0, img.naturalWidth * factor, img.naturalHeight * factor);
}
}
window.onresize = resizeCanvas;
resizeCanvas();
body {
background: lightgrey;
margin: 0;
}
.container {
position: relative;
background: blue;
width: 100vw;
height: 100vh;
}
#demo {
cursor: crosshair;
width: 100vw;
height: 100vh;
}
<script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>
<div class="container">
<canvas id="demo" width=640 height=640></canvas>
</div>
Here I'm waiting for the image to load. Then I can add the eventlistener for window resize and call the function resizeCanvas. This function will then resize the canvas and paint the image in the canvas.
The image must be repainted every time the window is resized.
To keep the aspect ratio the height of the image is calculated from the scaled width.
const canvas = document.getElementById('demo');
var ctx = canvas.getContext("2d"),
img = new Image(),
blurryImageSrc = "https://s9.postimg.cc/u9nsmzlwf/image.jpg";
img.src = blurryImageSrc;
img.addEventListener("load", e => {
window.addEventListener('resize', resizeCanvas);
resizeCanvas();
});
function paint() {
var newHeight = Math.round(img.width/canvas.width*img.height);
ctx.drawImage(img, 0, 0, img.width, newHeight, 0, 0, canvas.width, canvas.height);
}
function resizeCanvas() {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
paint();
}
body {
background: lightgrey;
margin: 0;
}
.container {
position: relative;
background: blue;
width: 100vw;
height: 100vh;
}
#demo {
cursor: crosshair;
width: 100vw;
height: 100vh;
}
<div class="container">
<canvas id="demo" width="640" height="640"></canvas>
</div>

setTransform and Scale to fit Canvas for the given image

I have an image on Canvas.
After rendering an image, I rotate the canvas to 90 degrees clockwise and then have to scale to fit the canvas.
Scaling an image to fit on canvas
Resize image and rotate canvas 90 degrees
there is so many solutions available but unfortunately none of them worked for me yet
This is what I have right now.
width = 300; // after rotation
height = 400; // after rotation
var scale = width / img.height; // how much to scale the image to fit
console.log(scale);
canvas.width = width;
canvas.height = height;
ctx.setTransform(
0, scale, // x axis down the screen
-scale, 0, // y axis across the screen from right to left
width, // x origin is on the right side of the canvas
0 // y origin is at the top
);
ctx.drawImage(img, 0, 0);
ctx.setTransform(1, 0, 0, 1, 0, 0); // restore default
Currently, canvas is rotating fine but image is not fitting or scaling properly according to canvas width and height.
Original image is large, I want to scale it to fit 300x400 on canvas.
Original Image -
Actual result image looks like this which is not scaling full image -
To fit an image to a display area, use the min scale to fit height or fit width.
// size of canvas
const width = 100;
const height = 298;
// image is assumed loaded and ready to be drawn
// Get min scale to fit
var scale = Math.min(width / image.width , height / image.height);
// you can also scale to fill using max
// var scale = Math.max(width / image.width , height / image.height);
canvas.width = width;
canvas.height = height;
// // draw image from top left corner
ctx.setTransform(scale, 0, 0, scale, 0, 0);
ctx.drawImage(image, 0, 0);
ctx.setTransform(1, 0, 0, 1, 0, 0); // restore default
Update
Re comment
As you are having trouble, and I am still not sure what you what the image to do, here is a general purpose function to rotate image in 90 deg steps, and scales to fit, fill, and natural. (Demo version in bottom snippet a little less verbose)
// img image to draw
// rot 1 unit = 90 deg. scaleType fit or fill
function drawRotatedImage(img, rot = 1, scaleType = "fit") {
const w = img.naturalWidth;
const h = img.naturalHeight;
// direction of xAxis
var xAxisX = Math.cos(rot * Math.PI / 2);
var xAxisY = Math.sin(rot * Math.PI / 2);
// modified transform image width and height to match the xAxis
const tw = Math.abs(w * xAxisX - h * xAxisY);
const th = Math.abs(w * xAxisY + h * xAxisX);
var scale = 1;
if (scaleType === "fit") {
scale = Math.min(canvas.width / tw, canvas.height / th);
} else if (scaleType === "fill") {
scale = Math.max(canvas.width / tw, canvas.height / th);
}
xAxisX *= scale;
xAxisY *= scale;
// Rotate scale to match scaleType . Center on canvas
ctx.setTransform(xAxisX, xAxisY, -xAxisY, xAxisX, canvas.width / 2, canvas.height / 2);
// Image drawn offset so center is at canvas center
ctx.drawImage(img, -w / 2, -h / 2, w, h);
}
And to make sure the following is a tested runing example using your image.
const canWidth = 300;
const canHeight = 400;
const rot = 1;
const scaleType = "fit";
const PI90 = Math.PI / 2;
function drawRotatedImage(img, rot, scale) {
ctx.setTransform(1, 0, 0, 1, 0, 0);
ctx.clearRect(0, 0, canvas.width, canvas.height);
const w = img.naturalWidth, h = img.naturalHeight;
var xAX = Math.cos(rot *= PI90 ), xAY = Math.sin(rot);
const tw = Math.abs(w * xAX - h * xAY);
const th = Math.abs(w * xAY + h * xAX);
scale = Math[scale === "fit" ? "min" : "max" ](canvas.width / tw, canvas.height / th);
xAX *= scale;
xAY *= scale;
ctx.setTransform(xAX, xAY, -xAY, xAX, canvas.width / 2, canvas.height / 2);
ctx.drawImage(img, -w / 2, -h / 2, w, h);
}
// Stuff for demo and test. unrelated to answer.
const ctx = canvas.getContext("2d");
// size canvas
canvas.width = canWidth;
canvas.height = canHeight;
const img = new Image;
const tests = [[0,"fit"], [1,"fit"], [2,"fit"], [3,"fit"],[0,"fill"], [1,"fill"], [2,"fill"], [3,"fill"]];
tests.clicker = 0;
img.src = "https://i.stack.imgur.com/SQmuv.jpg";
img.addEventListener("load", () => {
info.textContent = ((rot % 4) * 90) + "deg CW scaled to " + scaleType.toUpperCase() + " click canvas to rotate and switch scales";
drawRotatedImage(img, rot, scaleType)
});
canvas.addEventListener("click",() => {
const test = tests[tests.clicker++ % tests.length];
info.textContent = ((test[0] % 4) * 90) + "deg CW scaled to " + test[1].toUpperCase();
drawRotatedImage(img, ...test)
});
body {
font-family: "arial black";
}
canvas {
border: 1px solid black;
position: absolute;
top: 30px;
left: 10px;
}
#info {
position: absolute;
top: 2px;
left: 10px;
}
<canvas id="canvas"></canvas>
<div id="info"></div>

How to scale an image for a canvas pattern?

I want to fill a Canvas with an Image and scale it to a certain width beforehand.
I am trying to achieve an effect where an image in the foreground of the canvas can be erased with the mouse to view an image in the background. This is why I need to use a pattern to fill my canvas instead of just using drawImage(). Everything works apart from the scaling of the foreground image. Here is my code for generating the pattern:
var blueprint_background = new Image();
blueprint_background.src = "myfunurl";
blueprint_background.width = window.innerWidth;
blueprint_background.onload = function(){
var pattern = context.createPattern(this, "no-repeat");
context.fillStyle = pattern;
context.fillRect(0, 0, window.innerWidth, 768);
context.fill();
};
This does exactly what it should do, except that the image keeps its original size.
As you see, I want the image to scale to window.innerWidth (which has the value 1920 when logging it).
If needed, I can provide the rest of the code, but since the error is most likely in this snippet, I decided not to post the rest.
EDIT: Here is my full code with the suggested changes. The front ground image now displays over the full width, however the erasing does not work anymore.
JavaScript (Note that I use jQuery instead of $):
jQuery(document).ready(function() {
var cwidth = window.innerWidth;
var cheight = 768;
function createCanvas(parent, width, height) {
var canvas = {};
canvas.node = document.createElement('canvas');
canvas.context = canvas.node.getContext('2d');
canvas.node.width = width || 100;
canvas.node.height = height || 100;
parent.appendChild(canvas.node);
return canvas;
}
function init(canvas, fillColor) {
var ctx = canvas.context;
canvas.isDrawing = true;
jQuery('#canvas').children().css('position:absolute; top: ' + jQuery('#Top_bar').height() + 'px');
// define a custom fillCircle method
ctx.fillCircle = function(x, y, radius, fillColor) {
this.fillStyle = fillColor;
this.beginPath();
this.moveTo(x, y);
this.arc(x, y, radius, 0, Math.PI * 2, false);
this.fill();
};
// bind mouse events
canvas.onmousemove = function(e) {
if (!canvas.isDrawing) {
return;
}
var x = e.pageX - this.offsetLeft;
var y = e.pageY - jQuery('#Top_bar').outerHeight();
var radius = 30;
var fillColor = '#ff0000';
ctx.globalCompositeOperation = 'destination-out';
ctx.fillCircle(x, y, radius, fillColor);
};
}
var container = document.getElementById('canvas');
jQuery('#canvas').css('position:absolute; top: ' + jQuery('#Top_bar').height() + 'px');
var canvas = createCanvas(container, cwidth, cheight);
init(canvas, '#ddd');
var fgimg = document.getElementById("fgimg");
fgimg.width = cwidth;
var context = canvas.node.getContext("2d");
let canvasP = document.getElementById("pattern");
canvasP.width = window.innerWidth;
canvasP.height = 768;
let ctxP = canvasP.getContext("2d");
ctxP.drawImage( fgimg, 0, 0,window.innerWidth,768 );
context.fillStyle = context.createPattern(canvasP,"no-repeat");
context.fillRect(0,0, canvas.width, canvas.height);
});
CSS:
#canvas {
background:url(http://ulmke-web.de/wp-content/uploads/2019/01/Header-6.jpg);
background-repeat: no-repeat;
background-size: cover;
background-position: center center;
width: 100%;
height: 768px;
}
HTML:
<div id="canvas">
<canvas id="pattern">
</div>
<div style="display:none">
<img id="fgimg" src=" http://ulmke-web.de/wp-content/uploads/2019/01/Header-5.jpg">
</div>
I would use two canvases. On the first one you draw your image and you use this canvas as an image to create the pattern. In order to scale the image you scale the size of the first canvas #pattern in my example.
For example you can do this for a 10/10 image:
canvasP.width = 10;
canvasP.height = 10;
ctxP.drawImage( redpoint, 2.5, 2.5 );
or you can do this for a 20/20 image:
canvasP.width = 20;
canvasP.height = 20;
ctxP.drawImage( redpoint, 5, 5,10,10 );
Furthermore, in my example I'm adding a little margin around the image.
let canvasP = document.getElementById("pattern");
if (canvasP && canvasP.getContext) {
let ctxP = canvasP.getContext("2d");
/*canvasP.width = 10;
canvasP.height = 10;
ctxP.drawImage( redpoint, 2.5, 2.5 ); */
canvasP.width = 20;
canvasP.height = 20;
ctxP.drawImage( redpoint, 5, 5,10,10 );
}
let canvas1 = document.getElementById("canvas");
if (canvas1 && canvas1.getContext) {
let ctx1 = canvas1.getContext("2d");
if (ctx1) {
ctx1.fillStyle = ctx1.createPattern(canvasP,"repeat");
ctx1.fillRect(0,0, canvas1.width, canvas1.height);
}
}
canvas{border:1px solid}
<img id="redpoint" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUA AAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO 9TXL0Y4OHwAAAABJRU5ErkJggg==">
<canvas id="pattern"></canvas>
<canvas id="canvas"></canvas>
I hope it helps.

Image following a mouse on a canvas

I want to have an image follow the mouse around the canvas, which is fairly easy, but the catch is that I want my canvas to change with screen resolution (it is set using CSS to be 70vw).
When the resolution decreases and the window becomes smaller this means that using a normal method of using clientX doesn't work.
My code so far is this:
var mouseX = e.clientX/document.documentElement.clientWidth * 1920;
var mouseY = e.clientY/document.documentElement.clientHeight * 943;
This tries to convert the users clientX into the value it would be on a 1920x1080 monitor.
However, this isn't really accurate and doesn't work very well on even 1920x1080 monitors. Any help would be appreciated.
You can't scale the canvas using CSS in the way that you think. A canvas is basically a more advanced image. Scaling the canvas via CSS just stretches the canvas the same way an image would stretch. To change the canvas height and width, you need to change it's height and width attributes in the tag or via code. This will physically change the canvas to the size that you want without scaling and/or stretching.
That being said, we can use this to watch for window size changes and resize the canvas when the window changes.
window.addEventListener('resize', e => {
canvas.width = window.innerWidth
canvas.height = window.innerHeight
})
With some basic math, we can calculate what a 70% width would be, it would be done like this
window.addEventListener('resize', e => {
canvas.width = window.innerWidth * 0.7
canvas.height = window.innerHeight
})
The next thing we need to do is get the local position of the mouse on the canvas, which can be done using mousePosition - canvasOffset like this
let x = e.clientX - canvas.offsetLeft
let y = e.clientY - canvas.offsetTop
When all is said and done, we end up with something like this (To see it in action press run then click on Full Page and you will see the canvas resize):
const canvas = document.querySelector('canvas')
const ctx = canvas.getContext('2d')
// Set the inital height and width of the canvas
canvas.width = window.innerWidth
canvas.height = window.innerHeight
canvas.addEventListener('mousemove', e => {
ctx.clearRect(0, 0, canvas.width, canvas.height)
// Get the local x/y coordinates of the mouse on the canvas
let x = e.clientX - canvas.offsetLeft
let y = e.clientY - canvas.offsetTop
// Draw a dot where the mouse is
ctx.beginPath();
ctx.arc(x, y, 10, 0, 2 * Math.PI, false);
ctx.fillStyle = 'white';
ctx.fill();
})
// Update the height and width when the window size changes
window.addEventListener('resize', e => {
canvas.width = window.innerWidth
canvas.height = window.innerHeight
})
body {
padding: 0;
margin: 0;
}
canvas {
background-color: black;
display: block;
}
<canvas></canvas>
In this example below, we use a canvas that is 70% the width and height of the screen and center it with CSS. However, we never touch the height/width with css because it will mess up the canvas' coordinate system. This part is done with JavaScript.
const canvas = document.querySelector('canvas')
const ctx = canvas.getContext('2d')
// Set the inital height and width of the canvas
canvas.width = window.innerWidth * 0.7
canvas.height = window.innerHeight * 0.7
canvas.addEventListener('mousemove', e => {
ctx.clearRect(0, 0, canvas.width, canvas.height)
// Get the local x/y coordinates of the mouse on the canvas
let x = e.clientX - canvas.offsetLeft
let y = e.clientY - canvas.offsetTop
// Draw a dot where the mouse is
ctx.beginPath();
ctx.arc(x, y, 10, 0, 2 * Math.PI, false);
ctx.fillStyle = 'white';
ctx.fill();
})
// Update the height and width when the window size changes
window.addEventListener('resize', e => {
canvas.width = window.innerWidth * 0.7
canvas.height = window.innerHeight * 0.7
})
body {
padding: 0;
margin: 0;
}
canvas {
background-color: black;
display: block;
position: absolute;
left: 0;
right: 0;
bottom: 0;
top: 0;
margin: auto;
}
<canvas></canvas>
I took my snippet from my answer to create a full screen canvas.
I added this for mouse movement:
let User = { x: 0, y: 0 };
//controles if the mouse is moving
window.addEventListener(
"mousemove",
e => {
User.x = e.clientX;
User.y = e.clientY;
},
false
);
Uncomment: cvs.ctx.drawImage(image, User.x, User.y); in the ShowImage() function to draw an image at the mouse x and y position.
Mind to replace the path of the image source: image.src = "Your/Path/To/Image.png";
/**
* #author RensvWalstijn. GitHub: https://github.com/RensvWalstijn
* Sets the canvas properties.
* #param {object} Cvs Give the html canvas Id.
* #param {boolean} Fullscreen Change the canvas fullscreen default false.
* #param {string} Dimension Change the canvas dimension default "2d".
* #return {object}
*/
function NewCanvas(cvs, fullscreen, dimension) {
if (!dimension) dimension = "2d";
var ctx = cvs.getContext(dimension);
if (fullscreen) {
cvs.style.position = "fixed";
cvs.style.left = cvs.x = 0;
cvs.style.top = cvs.y = 0;
} else {
var rect = cvs.getBoundingClientRect();
cvs.x = rect.left;
cvs.y = rect.top;
}
cvs.ctx = ctx;
cvs.dimension = dimension;
cvs.fullscreen = fullscreen;
return cvs;
}
/**
* #author RensvWalstijn. GitHub: https://github.com/RensvWalstijn
* Updates the canvas width and hight.
* #param {object} Cvs NewCanvas() object.
* #param {boolean} Clear Change the canvas clear default true.
*/
function UpdateCvs(cvs) {
if (cvs.fullscreen) {
//if the width is not the same resize the canvas width
if (window.innerWidth != cvs.width) {
cvs.width = window.innerWidth;
}
//if the height is not the same resize the canvas height
if (window.innerHeight != cvs.height) {
cvs.height = window.innerHeight;
}
} else {
let rect = cvs.getBoundingClientRect();
cvs.x = rect.left;
cvs.y = rect.top;
}
}
function ClearCvs(cvs) {
if (cvs.dimension == "2d")
// set fillRect to clearRect to clear all of the canvas
// fillRect is used here to show the full canvas
cvs.ctx.fillRect(0, 0, cvs.width, cvs.height);
}
/**
* #author RensvWalstijn. GitHub: https://github.com/RensvWalstijn
* get html element by id.
* #param {string} id give the html element id.
* #return {object} document.getElementById(id);
*/
function GetId(id) { return document.getElementById(id) }
// To create your canvas object.
var canvas = NewCanvas(GetId("yourCanvasId"), true);
// If you want to update your canvas size use this:
window.addEventListener("resize", function() {
UpdateCvs(canvas);
});
let User = { x: 0, y: 0 };
//controles if the mouse is moving
window.addEventListener(
"mousemove",
e => {
User.x = e.clientX;
User.y = e.clientY;
},
false
);
// Set it to current width
UpdateCvs(canvas);
ClearCvs(canvas);
// create an image
let image = new Image();
image.src = "Your/Path/To/Image.png";
function ShowImage(cvs) {
// Use this line to draw your image.
// cvs.ctx.drawImage(image, User.x, User.y);
// Shows where your image will be drawn.
cvs.ctx.clearRect(User.x, User.y, 100, 100);
}
function Update() {
ClearCvs(canvas);
ShowImage(canvas);
// keeps it looping
window.requestAnimationFrame(Update)
}
// Init the loop
Update();
<canvas id="yourCanvasId"></canvas>

HTML5 Canvas scaling an image animation

I've created a straight forward linear HTML5 animation but I now want the image to continuously scale bigger then smaller until the end of the canvas' height.
What's the best way to do this?
Here's a JSFIDDLE
window.requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || window.oRequestAnimationFrame;
var canvas = document.getElementById('monopoly-piece');
var context = canvas.getContext('2d');
var img = document.createElement("img");
img.src = "images/tophat.png";
img.shadowBlur = 15;
img.shadowColor = "rgb(0, 0, 0)";
var xSpeed = 0; //px per ms
var ySpeed = 0.15;
function animate(nowTime, lastTime, xPos, yPos) {
// update
if ((img.style.height + yPos) > canvas.height) {
xPos = 0;
yPos = 0;
}
var timeDelta = nowTime - lastTime;
var xDelta = xSpeed * timeDelta;
var yDelta = ySpeed * timeDelta;
// clear
context.clearRect(0, 0, canvas.width, canvas.height);
// shadow
context.shadowOffsetX = 3;
context.shadowOffsetY = 7;
context.shadowBlur = 4;
context.shadowColor = 'rgba(0, 0, 0, 0.4)';
//draw img
context.drawImage(img, xPos, yPos);
if (yPos > canvas.height - img.height ) {
addMarker();
}
else {
requestAnimationFrame(
function(timestamp){
animate(timestamp, nowTime, xPos + xDelta, yPos + yDelta);
}
);
}
}
animate(0, 0, -10, 0);
Here is my current code which animates an image from the top of the canvas element to the bottom and then stops.
Thanks.
context.DrawImage has additional parameters that let you scale your image to your needs:
// var scale is the scaling factor to apply between 0.00 and 1.00
// scale=0.50 will draw the image half-sized
var scale=0.50;
// var y is the top position on the canvas where you want to drawImage your image
var y=0;
context.drawImage(
// draw img
img,
// clip a rectangular portion from img
// starting at [top,left]=[0,0]
// and with width,height == img.width,img.height
0, 0, img.width, img.height,
// draw that clipped portion of of img on the canvas
// at [top,left]=[0,y]
// with width scaled to img.width*scale
// and height scaled to img.height*scale
0, y, img.width*scale, img.height*scale
)
Here's an example you can start with and fit to your own design needs:
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var cw = canvas.width;
var ch = canvas.height;
var scale = 1;
var scaleDirection = 1
var iw, ih;
var y = 1;
var yIncrement = ch / 200;
var img = new Image();
img.onload = start;
img.src = "https://dl.dropboxusercontent.com/u/139992952/multple/sun.png";
function start() {
iw = img.width;
ih = img.height;
requestAnimationFrame(animate);
}
function animate(time) {
if (scale > 0) {
requestAnimationFrame(animate);
}
ctx.clearRect(0, 0, cw, ch);
ctx.drawImage(img, 0, 0, iw, ih, 0, y, iw * scale / 100, ih * scale / 100);
scale += scaleDirection;
if (scale > 100) {
scale = 100;
scaleDirection *= -1;
}
y += yIncrement;
}
$("#test").click(function() {
scale = y = scaleDirection = 1;
requestAnimationFrame(animate);
});
body{ background-color: ivory; }
canvas{border:1px solid red;}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<button id="test">Animate Again</button>
<br>
<canvas id="canvas" width=80 height=250></canvas>

Categories