This question already has an answer here:
Is the canvas empty?
(1 answer)
Closed 1 year ago.
I have a web page with two canvas elements stacked on top of each other. This is the basis for some functionality that lets me "erase" the top canvas to reveal an image loaded into the bottom canvas. The functionality works well.
What I'm trying to do now is trigger an event once the top canvas has been completely "erased" i.e. all of the pixels for the top context are transparent. I've found ways on SO to check a particular pixel's alpha value via getImageData but I'm trying to figure out a way to determine when the very last pixel's alpha value = 0.
I've included my code as well as an unfinished attempt to do this with a for and if loop (but this seems to not be the best approach). Another issue I've discovered is that when I use getImageData a thin grey border appears around two edges of my canvas.
Thanks for any help in advance!
window.onload = function() {
//Move speaker image across page
var speaker = document.getElementById('speaker');
speaker.onload = MoveElement(speaker, "right", 1000);
//Create canvases & contexts
var canvas = document.getElementById('canvas');
var ctxB = canvas.getContext('2d');
var canvas2 = document.getElementById('canvas2');
var ctxT = canvas2.getContext('2d');
//Get waterfall image object
var waterfall = document.getElementById('waterfall');
//Set canvas w&h properties
canvas.width = canvas2.width = .3*waterfall.width;
canvas.height = canvas2.height = .3*waterfall.height;
//Populate Bottom canvas with waterfall image
ctxB.drawImage(waterfall, 0, 0, canvas.width, canvas.height);
//Populate Top canvas with white rectangle
ctxT.fillStyle = "white";
ctxT.fillRect(0, 0, canvas2.width, canvas2.height);
//Make Top canvas "erasable"
canvas2.addEventListener('mousemove', event => {
var x = event.offsetX;
var y = event.offsetY;
const eraseSize = 125;
ctxT.clearRect(x-eraseSize/2, y-eraseSize/2, eraseSize, eraseSize);
})
//Attempt to trigger an event when all pixels of top canvas have alpha = 0
var imageDataTop = ctxT.getImageData(0, 0, canvas2.width, canvas2.height);
console.log(imageDataTop);
for (var i = 0; i < imageDataTop; i++) {
if (imageDataTop.data[i] = 0) {
//
}
}
}
function PlayAudio() {
document.getElementById('boyfriend').play();
}
//Move an element in a given direction a certain distance
function MoveElement(element, direction, totalDistance) {
//Determine horizontal or vertical direction
var LeftOrTop = (direction=="left" || direction=="right") ? "left" : "top";
//Set default frame distance
var frameDistance = 150;
//Adjust for backwards direction
if (direction=="left" || direction=="up") {
totalDistance *= -1;
frameDistance = -1;
}
//Get style data object for element
var elementStyle = window.getComputedStyle(element);
//Get Left or Top position of element
var positionValue = elementStyle.getPropertyValue(LeftOrTop).replace("px", "");
//Apply direction and distance change to element
var destination = (Number(positionValue) + totalDistance) + "px";
function MoveFrame() {
//If element reaches its destination...
if (elementStyle.getPropertyValue(LeftOrTop) == destination) {
//...stop the interval motion
clearInterval(movingFrames);
}
//Otherwise, continue the interval movement
else {
elementStyle = window.getComputedStyle(element);
positionValue = elementStyle.getPropertyValue(LeftOrTop).replace("px", "");
element.style[LeftOrTop] = (Number(positionValue) + frameDistance) + "px";
}
}
//Define time interval for movement
var movingFrames = setInterval(MoveFrame, 500);
}
#stack {
position: relative;
}
#stack canvas {
position: absolute;
display: block;
left: 50%;
transform: translateX(-50%);
margin-top: 150px;
}
#speaker {
position: absolute;
top: 20px;
animation-duration: 800ms;
animation-name: fadein;
animation-iteration-count: 10;
}
#keyframes fadein {
from {
opacity: 0.25;
}
50% {
opacity: 1;
}
to {
opacity: 0.25;
}
}
<!DOCTYPE html>
{% load static %}
<html>
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="{% static 'entrance/entrance.css' %}">
<script src="{% static 'entrance/entrance.js' %}"></script>
</head>
<body>
<p hidden>
<img src="{% static 'entrance/Waterfall.jpg' %}" alt="issue here" id="waterfall" />
</p>
<img src="{% static 'entrance/sound-icon.svg' %}" id="speaker" />
<p id="test"></p>
<audio id="boyfriend" source src="{% static 'entrance/02 30 Minute Boyfriend.mp3' %}" type="audio/mp3"></audio>
<div id="stack" onmouseenter="PlayAudio()">
<canvas id="canvas"></canvas>
<canvas id="canvas2"></canvas>
</div>
</body>
</html>
The dataimage that you get will be an array which has 4 bytes per point on the canvas the first 3 bytes of which are the RGB colors and the 4th one is the alpha.
If the color is transparent this 4th one will be 0. You will need to go through each time some erasing is done looking at every 4th byte in the array. If you come across one that isn't zero you will know that the erasing isn't complete.
This is quite a useful reference: https://www.w3schools.com/tags/canvas_getimagedata.asp
Related
Context
I have a canvas in which I draw an image. I would want to animate it: the image should be zoomed-in (rescaled) in all directions (vertical and horizontal expansions, towards the top, right, bottom, and left sides, all together and at the same time).
Expected results
At each iteration, the image goes x pixels towards the top and the bottom, the left, and the right sides of the canvas: it's a zoom. The pixels of the image that go beyond the canvas become not visible.
Actual results
At each iteration, the image goes x pixels towards the bottom and right sides of the canvas: it's a zoom. The pixels of the image that go beyond the canvas become not visible.
The problem
The image goes x pixels towards the bottom and right sides of the canvas instead of going x pixels towards the top and the bottom, the left, and the right sides of the canvas.
The question
How to make the image go x pixels towards the top and the bottom, the left, and the right sides of the canvas?
Minimal and Testable Example
How to test my example?
Download an image file titled "my_img.jpg", store it in a directory titled "my_dir". You can change the names; think to change them in the below source code too (line to be changed: var images = [ ....).
In this same directory, store my code in a file titled "index.HTML"
Open your browser to read "index.HTML"
Click on the "Start" button, the animation will begin and you will see my problem.
Sources
Comments
Note the use of ctx.drawImage(tmp_canvas, -(tmp_canvas.width - canvas.width)/2, -(tmp_canvas.height - canvas.height)/2);, which is censed to draw the zoomed image (from a new canvas named "tmp_canvas"), in the real canvas (whose context is ctx). I divide by 2 the difference between the zoomed image minus the real canvas, which normally corresponds to the fact to draw the zoomed image expanded towards left AND right. The same techniqye is used for top and bottom sides.
Clues
console.log(-(tmp_canvas.width - canvas.width)/2); shows 0, it should not. tmp_canvas.width should be the zoomed image's width. canvas.width should be the canvas' width.
index.HTML
<html>
<head>
<title>Creating Final Video</title>
</head>
<body>
<button id="start">Start</button>
<canvas id="canvas" width="3200" height="1608"></canvas>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script>
var images = ["my_dir/my_img.jpg"];
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var img = new Image();
function showSomeMedia() {
setImageListener();
setImageSrc();
}
function setImageSrc() {
img.src = images[0];
img.crossOrigin = "anonymous";
}
function imgOnLoad() {
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
ctx.restore();
var tmp_canvas = canvas.cloneNode();
var tmp_ctx = tmp_canvas.getContext("2d");
function zoomInCanvas() {
tmp_ctx.scale(1.001, 1.001);
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(
tmp_canvas,
-(tmp_canvas.width - canvas.width) / 2,
-(tmp_canvas.height - canvas.height) / 2
);
}
var i = 0;
const interval = setInterval(function() {
if (i == 100) {
clearInterval(interval);
}
tmp_ctx.clearRect(0, 0, tmp_canvas.width, tmp_canvas.height);
tmp_ctx.drawImage(canvas, 0, 0);
requestAnimationFrame(zoomInCanvas);
i++;
}, 1000);
}
function setImageListener() {
img.onload = function() {
imgOnLoad();
};
}
$("#start").click(function() {
showSomeMedia();
});
</script>
</body>
</html>
I don't know about how to solve some of the other problems with your code, however I can help with being able to rescale images in all directions.
$(document).ready(function(){
$('#resizable').resizable({
handles: 'n, e, s, w, ne, se, sw, nw'
});
});
#resizable {
width: 50%;
border: 1px solid black;
}
#resizable img {
width: 100%;
}
.ui-resizable-handle {
background: red;
border: 1px solid red;
width: 9px;
height: 9px;
z-index: 2;
}
.ui-resizable-se {
right: -5px;
bottom: -5px;
}
#resizable {
width: 150px;
height: 150px;
padding: 0.5em;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<link rel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
<img id="resizable" src="https://www.w3schools.com/w3css/img_lights.jpg">
Some of this code is from a question that I recently asked, and got an answer for.
In fact, the translate call doesn't change the tmp_canvas's image's dimensions. So I have to compute these new width and height and store them in parralel. The following code works perfectly:
<html>
<head>
<title>Creating Final Video</title>
</head>
<body>
<button id="start">Start</button>
<canvas id="canvas" width=3200 height=1608></canvas>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script>
var images = [
'my_dir/my_img.jpg'
];
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var img = new Image();
function showSomeMedia() {
setImageListener();
setImageSrc();
}
function setImageSrc() {
img.src = images[0];
img.crossOrigin = "anonymous";
}
function imgOnLoad() {
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
ctx.restore();
var tmp_canvas = canvas.cloneNode();
var tmp_ctx = tmp_canvas.getContext("2d");
var img_new_width = tmp_canvas.width;
var img_new_height = tmp_canvas.height;
function zoomInCanvas() {
img_new_width *= 1.001
img_new_height *= 1.001
tmp_ctx.scale(1.001, 1.001);
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(tmp_canvas, -(img_new_width - canvas.width)/2, -(img_new_height - canvas.height)/2);
}
var i = 0;
const interval = setInterval(function() {
if(i == 100) {
clearInterval(interval);
}
tmp_ctx.clearRect(0, 0, tmp_canvas.width, tmp_canvas.height);
tmp_ctx.drawImage(canvas, 0, 0);
requestAnimationFrame(zoomInCanvas);
i++;
}, 1000);
}
function setImageListener() {
img.onload = function () {
imgOnLoad();
};
}
$('#start').click(function() {
showSomeMedia();
});
</script>
</body>
</html>
I have a WEBGL canvas in which I am loading a 3d model. I also have a text in variable font which I create with createP. The problem is that I need the text to be between the background and the 3d model. But I can only have the text in front of the canvas (on top of 3d model). Is there any way I can achieve this?
Ps. Creating the text with text() does not allow me to have variable weight value.
let kalameh;
var cnv;
let img;
function preload(){
img = loadImage('assets/lines.png');
kalameh = loadModel('kalameh.obj');
}
function setup() {
cnv = createCanvas(700, 700, WEBGL);
var x = (windowWidth - width) / 2;
var y = (windowHeight - height) / 2;
cnv.position(x, y);
wordA = createP('my text');
}
function draw() {
background(220, 220, 220);
wordA.style('font-size', '110px');
wordA.style('font-weight', '200');
wordA.style('font-stretch', '200%');
wordA.style('align', 'center');
wordA.position(40,25);
image(img, -1*windowWidth/2, -1*windowHeight/2, windowWidth, windowHeight);
translate (0,0,100);
pointLight(255,255,255, -350,0,400);
pointLight(255,255,255, 350,0,400);
ambientMaterial(255);
noStroke();
scale (0.25);
model(kalameh);
}
You could create two sketches (and thus two canvases) and assign z-index properties of -1 and 1 to make them foreground and background sketches, respectively. You can then sandwich the paragraph element that you created between the two canvases by giving it a z-index of 0. You can then make the foreground sketch transparent by calling clear() at the beginning of the draw loop.
In the code snippet below, the 3D object is in front of the text paragraph, whereas the white square of the "background" is behind the text.
let testP;
new p5(function(p){
p.setup = function() {
const foregroundCanvas = p.createCanvas(400, 400, p.WEBGL);
foregroundCanvas.id("foregroundSketch");
foregroundCanvas.position(0, 0);
p.normalMaterial();
testP = p.createP("In front of background / behind object ")
testP.position(95, 115);
testP.id("sandwichedParagraph")
p.angleMode(p.RADIANS)
}
p.draw = function() {
p.clear();
//drag to move the world.
// p.orbitControl();
p.push();
let rotateAngle = p.sin(p.frameCount/50);
p.rotateX(rotateAngle/2);
p.rotateY(-rotateAngle);
p.rotateZ(rotateAngle);
p.box(200, 100, 40);
p.pop();
}
}, "foregroundSketch");
new p5(function(p){
p.setup = function() {
const backgroundCanvas = p.createCanvas(400, 400);
// backgroundCanvas.parent("wrapper")
backgroundCanvas.position(0, 0);
backgroundCanvas.id("backgroundSketch")
p.noStroke();
}
p.draw = function() {
p.background(200);
let shiftAmount = p.map(p.sin(p.frameCount/60), -1, 1, 80, 310);
p.rect(shiftAmount, 120, 40, 40);
}
}, "backgroundSketch");
html, body {
margin: 0;
padding: 0;
}
/* The sketch with the white square has z-index of -1 (background!) */
#backgroundSketch {
z-index: -1;
position: absolute;
}
/* The <p> has z-index of 0 (sandwiched between both sketches)*/
#sandwichedParagraph {
z-index: 0;
position: absolute;
}
/* The sketch with the object has z-index of 1 (foreground!)*/
#foregroundSketch {
z-index: 1;
position: absolute;
}
<!DOCTYPE html>
<html lang="en">
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.10.2/p5.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.10.2/addons/p5.sound.min.js"></script>
<link rel="stylesheet" type="text/css" href="style.css">
<meta charset="utf-8" />
</head>
<body>
<script src="sketch.js"></script>
</body>
</html>
Once I upload an image I make 2 clicks over it. That's how (x1,y1) and (x2,y2) are obtained. Given these 4 numbers I waanna draw a rectangle over the image by means for example of P5.js. Then I should say rect(x1,y1,x2,y2) but this never happens. How can I deal with this problem (maybe not by P5)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Vue Test</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap.min.css">
</head>
<body>
<div id="app">
<button v-on:click="isHidden = false">Load img</button>
<img onclick="showCoords(event)" v-if="!isHidden" v-bind:src="src[z]"></img>
</div>
<script>
var test = new Vue({
el: '#app',
data: {
src: ["cat.jpg", "dog.jpg"],
z: Math.round(Math.random()),
isHidden: true,
}
})
var k=0;
var koors = [];
var flag = false;
function showCoords(event) {
if (k===0){
koors[0] = event.clientX;
koors[1] = event.clientY;
k+=1;
} else if (k===1){
flag = true;
koors[2] = event.clientX;
koors[3] = event.clientY;
}
if ((koors[3] != 0) && (flag)){
console.log(koors)
}
}
//p5
function setup(){
}
function draw(){
console.log(koors[0],koors[1],koors[2],koors[3]);
rect(koors[0],koors[1],koors[2],koors[3])
}
</script>
<script src="p5.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vue#2.5.13/dist/vue.js"></script>
<script src="js.js"></script>
</body>
</html>
P5 needs a canvas for rendering. If you don't initialize one, it creates it himself (and you are in trouble).
Also, P5 is powerful library, it has tools for events, image processing etc. For current example I wouldn't use any other library (vue).
I created canvas on top of the image (css) and rest is playing with P5.
var test = new Vue({
el: '#app',
data: {
src: [ "https://i.stack.imgur.com/XZ4V5.jpg", "https://i.stack.imgur.com/7bI1Y.jpg"],
z: Math.round(Math.random()),
isHidden: false,
}
})
var recStart;
var coords = {};
/*******************************************************************
* P5 setup
* run once, use for initialisation.
*
* Create a canvas, change dimensions to
* something meaningful(like image dim)
********************************************************************/
function setup(){
var canvas = createCanvas(480, 320);
canvas.parent('app');
}
/*******************************************************************
* P5 draw
* endless loop.
*
* Lets redraw rectangle until second click.
********************************************************************/
function draw(){
if(recStart)
drawRect();
}
/*******************************************************************
* drawRect
*
* Draw a rectangle. mouseX and mouseY are P5 variables
* holding mouse current position.
********************************************************************/
function drawRect(){
clear();
noFill();
stroke('red');
rect(coords.x, coords.y, mouseX-coords.x, mouseY-coords.y);
}
/*******************************************************************
* P5 mouseClicked
*
* P5 event. Again, mouseX and mouseY are used.
********************************************************************/
mouseClicked = function() {
if (mouseButton === LEFT) {
if(!recStart){ // start rectangle, give initial coords
coords.x = mouseX;
coords.y = mouseY;
recStart = true; // draw() starts to draw
} else {
recStart = false; // stop draw()
drawRect(); // draw final rectangle
coords = {}; // clear coords
}
}
};
canvas {
width: 500px;
height:500px;
z-index: 2;
border:1px solid;
position:absolute;
left: 0;
top: 0;
}
img {
z-index: 1;
position: absolute;
border: 2px solid black;
left: 0;
top: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.7.2/p5.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.13/vue.js"></script>
<div id="app">
<img id="pilt" v-if="!isHidden" v-bind:src="src[z]"></img>
</div>
It seems that you don't call draw anywhere. You may also consider draw the image into a canvas and than draw rect in this canvas. Checking if koors[3] != 0 is really risky as user may click at 0th pixel.
We need to implement a screen saver which should have 3 images.
The bottom image should be fixed.
Middle image will be moving.
Top image will be moving at relatively higher speed.
We created 3 divs to handle the images as below.
<!DOCTYPE html>
<html>
<head>
<title>Moving Screen Saver</title>
<style>
html, body {
position: relative;
height: 100%;
width: 100%;
}
#bgimg {
background-image: url("background/moon_bg.png");
position: absolute;
background-repeat: no-repeat;
background-size: cover;
width: 100%;
height: 100%;
}
.animator {
width: 100vw;
height: 100vh;
}
#poster {
background-image: url("posters/gladiator.png");
position: absolute;
background-position:right 20px bottom 20px;
background-repeat: no-repeat;
width: 100%;
height: 100%;
background-color: transparent;
}
</style>
</head>
<body>
<div id="bgimg"></div>
<div class="animator"></div>
<div id="poster"></div>
<script>
var poster = document.getElementById('poster');
var animate;
function moveRight()
{
poster.style.left = poster.style.left || 0;
poster.style.left = parseInt(poster.style.left) - 10 + 'px';
requestAnimationFrame(moveRight);
}
moveRight();
function rollAnimate(element, url) {
if(!element instanceof HTMLElement)
return;
var canvas = document.createElement('canvas');
var ctx = canvas.getContext('2d');
var img = new Image();
var x = 0;
var pattern;
var framesPerSec = 10;
img.onload = startDrawing;
img.onerror = console.error;
img.src = url;
canvas.style.cssText = 'position:absolute;'
function startDrawing() {
element.insertBefore(canvas, element.firstChild);
pattern = ctx.createPattern(img, 'repeat');
resize();
anim();
window.addEventListener('resize', resize, {passive: true});
}
function anim() {
x = (x - 1) % img.width;
ctx.setTransform(1,0,0,1,x,(canvas.height) - (img.height));
ctx.fill();
setTimeout(function() {
requestAnimationFrame(anim);
}, 1000 / framesPerSec);
}
function resize(evt) {
canvas.width = element.offsetWidth;
canvas.height = element.offsetHeight;
ctx.fillStyle = pattern;
ctx.rect(0, canvas.height - img.height, canvas.width, img.height);
ctx.globalCompositeOperation = 'copy';
}
}
rollAnimate(document.querySelector('.animator'), 'buildings/mod_cropped.png');
</script>
</body>
</html>
lower image - bgimg
middle image - animator
top image - poster
We are able to see lower image which is fixed and middle image which is moving. But poster image is not visible. We are thinking that this is because when we are trying to paint middle image it is replacing top image. Can any one please help me to fix this issue.
Below is jsfiddle page
https://jsfiddle.net/n76epste/10/
So when I look, you are actually getting all 3 images. Your issue is the poster moves off screen super fast and never returns.
bgimg is static and covered by animator (you can see this by deleting animator)
animator is animated and repeating and cover bgimage
poster is given an ever updating style="left: -***px" where * is an increasing number. (You can see this for a split second when loading the page if you hide bgimg and animator with css)
This is because of Z-index. I changed z-index of poster dev tp the highest to fix the problem.
Why does html5 canvas become very slow when I draw 2000 images and more ?
How can I optimise it?
Here's a demo that I've made, disable the "safe mode" by left clicking on the canvas and start moving your mouse until you get ~2000 images drawn
var img = new Image()
img.src = "http://i.imgur.com/oVOibrL.png";
img.onload = Draw;
var canvas = $("canvas")[0];
var ctx = canvas.getContext("2d")
var cnv = $("canvas");
var draw = [];
$("canvas").mousemove(add)
function add(event) {
draw.push({x: event.clientX, y: event.clientY})
}
canvas.width = cnv.width();
canvas.height = cnv.height();
var safe = true;
cnv.contextmenu(function(e) { e.preventDefault() })
cnv.mousedown(function(event) {
if(event.which == 1) safe = !safe;
if(event.which == 3) draw = []
});
function Draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
requestAnimationFrame(Draw);
for(var i of draw) {
ctx.drawImage(img, i.x, i.y)
}
if(safe && draw.length > 300) draw = []
ctx.fillText("Images count: "+ draw.length,10, 50);
ctx.fillText("Left click to toggle the 300 images limit",10, 70);
ctx.fillText("Right click to clear canvas",10, 90);
}
Draw();
body {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
position: absolute;
}
canvas {
position: absolute;
width: 100%;
height: 100%;
z-index: 99999999999;
cursor: none;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<canvas></canvas>
Codepen: http://codepen.io/anon/pen/PpeNme
The simple way with the actual code :
Don't redraw all your images at this rate.
Since in your example, the images are static, you actually don't need to redraw everything every frame : Just draw the latest ones.
Also, if you've got other drawings occurring (e.g your texts), you may want to use an offscreen canvas for only the images, that you'll redraw on the onscreen canvas + other drawings.
var img = new Image()
img.src = "http://i.imgur.com/oVOibrL.png";
img.onload = Draw;
var canvas = $("canvas")[0];
var ctx = canvas.getContext("2d")
var cnv = $("canvas");
var draw = [];
$("canvas").mousemove(add)
function add(event) {
draw.push({
x: event.clientX,
y: event.clientY
})
}
canvas.width = cnv.width();
canvas.height = cnv.height();
// create an offscreen clone of our canvas for the images
var imgCan = canvas.cloneNode();
var imgCtx = imgCan.getContext('2d');
var drawn = 0; // a counter to know how much image we've to draw
var safe = true;
cnv.contextmenu(function(e) {
e.preventDefault()
})
cnv.mousedown(function(event) {
if (event.which == 1) safe = !safe;
if (event.which == 3) draw = []
});
function Draw() {
// clear the visible canvas
ctx.clearRect(0, 0, canvas.width, canvas.height);
requestAnimationFrame(Draw);
if (draw.length) { // onmy if we've got some objects to draw
for (drawn; drawn < draw.length; drawn++) { // only the latest ones
let i = draw[drawn];
// draw it on the offscreen canvas
imgCtx.drawImage(img, i.x, i.y)
}
}
// should not be needed anymore but...
if (safe && draw.length > 300) {
draw = [];
drawn = 0; // reset our counter
// clear the offscren canvas
imgCtx.clearRect(0, 0, canvas.width, canvas.height);
}
// draw the offscreen canvas on the visible one
ctx.drawImage(imgCan, 0, 0);
// do the other drawings
ctx.fillText("Images count: " + draw.length, 10, 50);
ctx.fillText("Left click to toggle the 300 images limit", 10, 70);
ctx.fillText("Right click to clear canvas", 10, 90);
}
Draw();
body {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
position: absolute;
}
canvas {
position: absolute;
width: 100%;
height: 100%;
z-index: 99999999999;
cursor: none;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<canvas></canvas>
Now, if you need these images to be dynamic (i.e move at every frame), you may consider using imageDatas.
You can see this original post by user #Loktar, which explains how to parse your drawn image imageData, and then redraw it pixel per pixel on the visible canvas' imageData. You can also see this follow-up Q/A, which provides a color implementation of Loktar's idea.
On small images, this actually improves drastically the performances, but it has the huge inconvenient to not support alpha channel multiplication. You will only have fully transparent and fully opaque pixels. An other cons is that it may be harder to implement, but this is just your problem ;-)