Clear HTML canvas [duplicate] - javascript

This question already has answers here:
clearRect function doesn't clear the canvas
(5 answers)
Closed 3 years ago.
I want an HTML canvas that displays mouse position on its JS grid on mouse move, but I can't seem to clear my canvas, I tried using ctx.clearRect(0,0 canvas.width, canvas.height) and clearing with a click, but it somehow remembers the previous draw it had. I want only one black square to be displayed on canvas at a time depending on the mouse position. Heres demo on code pen and some code
<canvas id="myMap" style="width: 300px;height: 300px;background-color: beige;"></canvas>
<script>
var findDivisible = (x, scale) => {
while (x % scale !== 0 && x > 0) {
x = x - 1;
};
return x
};
var map = document.getElementById("myMap");
map.width = 300;
map.height = 300;
var mapContext = document.getElementById("myMap").getContext("2d");
map.addEventListener("mousemove", function(e) {
mapContext.clearRect(0, 0, map.width, map.height);
mapContext.rect(findDivisible(e.clientX - map.offsetLeft, 50), findDivisible(e.pageY - map.offsetTop, 50), 50, 50);
mapContext.stroke();
});
map.addEventListener("click", function() {
mapContext.clearRect(0, 0, 500, 500);
})
</script>

You're not starting a new stroke for each rectangle, but "piling them up" so they get re-re-redrawn with .stroke().
Use .beginPath():
function findDivisible(x, scale) {
while (x % scale !== 0 && x > 0) {
x = x - 1;
}
return x;
}
var map = document.getElementById("myMap");
map.width = 300;
map.height = 300;
var mapContext = map.getContext("2d");
map.addEventListener("mousemove", function(e) {
mapContext.clearRect(0, 0, map.width, map.height);
mapContext.beginPath();
mapContext.rect(
findDivisible(e.clientX - map.offsetLeft, 50),
findDivisible(e.pageY - map.offsetTop, 50),
50,
50,
);
mapContext.stroke();
});
map.addEventListener("click", function() {
mapContext.clearRect(0, 0, 500, 500);
});
<canvas id="myMap" style="width: 300px;height: 300px;background-color: beige;"></canvas>

You can try this:
map.addEventListener("mousemove", function(e){
map.width = map.width;
mapContext.rect(findDivisible(e.clientX-map.offsetLeft,50) , findDivisible(e.pageY - map.offsetTop,50), 50, 50);
mapContext.stroke();
});
map.addEventListener("click", function(){
map.width = map.width;
});
One of the ways that is implicitly endorsed in the spec and often used in people’s apps to clear a canvas
https://dzone.com/articles/how-you-clear-your-html5

Related

how to add css ROTATEX() and ROTATEY() to image in canvas [duplicate]

This question already has answers here:
how to skew image like this
(5 answers)
Closed 10 months ago.
i am trying to make a simple canvas game in jquery,
basically, i have an infinite loop that constantly draws the player and background:
loop = () => {
$('#player').css("transform", "rotateX("+xr+"deg) rotateY("+yr+"deg)");
ctx.fillStyle = 'gray';
ctx.fillRect(0,0,400,400);
ctx.drawImage(player, x, y, 50, 50);
window.setTimeout(loop, 1);
}
And then i have a switch statement that alters the player's XR and YR when they push the A and D buttons:
$(document).ready(()=>{
loop();
$(document).bind('keydown',(e)=>{
switch(e.keyCode){
case 65:
//moving left (altering the xr and yr values)
break;
case 87:
//moving up (altering the y and x values based on SIN and COS and player's XR and YR)
break;
etc...
}
});
});
i can tell it's detecting the key pushes, because when i run CONSOLE.LOG() in the switch statement, it prints something to the console,
so clearly this is a problem with images drawn to a canvas not keeping their css styling.
if anybody can figure out how to keep an image's css when drawing it to a canvas, or alter an image draw in a canvas's css, that would be great.
and no, i didn't find any answers anywhere else. this question doesn't seem to have been asked yet.
EDIT: xr and yr are XROTATION and YROTATION variables.
apparently, images don't keep their css in canvases, and there is no way to add this. never mind, guess i'll try something else.
You don't need CSS, you can do everything with the Canvas, you have translate and rotate methods. Here a very basic example:
const ctx = canvas.getContext("2d");
const { width: w, height: h } = canvas;
let x = (w - 50) >> 1, y = (h - 50) >> 1;
let angle = 0;
const Keys = {
pressed: {},
handleEvent({type, code}) {
this.pressed[code] = type === "keydown"
}
}
document.addEventListener("keydown", Keys);
document.addEventListener("keyup", Keys);
function update() {
if (Keys.pressed["ArrowLeft"]) {
--angle;
}
if (Keys.pressed["ArrowRight"]) {
++angle;
}
}
function draw() {
ctx.fillStyle = "black";
ctx.fillRect(0, 0, w, h);
ctx.translate(x + 25, y + 25);
ctx.rotate(angle * Math.PI / 180 );
ctx.drawImage(player, -25, -25, 50, 50);
ctx.setTransform(1, 0, 0, 1, 0, 0);
}
let player = new Image();
player.src = "https://1.bp.blogspot.com/-ddkcnhuU07g/Vllq4DNohkI/AAAAAAAACHo/p8d9SZxOy-w/s1600/mship1.png";
requestAnimationFrame(function render() {
update();
draw();
requestAnimationFrame(render);
})
<canvas id="canvas" width="320" height="240"></canvas>

How to store and restore canvas to use it again?

I am using PDF.js to show PDF in browser. PDF.js uses canvas to render PDF. I have js scripts that draws the lines on the canvas when user double clicks on the canvas. It also adds X check mark to remove the already drawn line.
based on my research i cannot simply just remove the line from the canvas because underneath pixels are gone when you draw something on it. To get it working i have to store lines and then clear canvas and re-load canvas and re-draw lines
Issue
I am not able to store canvas and restore canvas. When i click on X i was able to get lines re-drawn but canvas does not get restored. Canvas remains blank
Run the demo in full page
$(function () {
var $canvas = $("#myCanvas");
var canvasEl = $canvas.get(0);
var ctx = canvasEl.getContext("2d");
var lines = [];
var backupCanvas = document.createElement("canvas");
var loadingTask = pdfjsLib.getDocument('https://raw.githubusercontent.com/mozilla/pdf.js/ba2edeae/web/compressed.tracemonkey-pldi-09.pdf');
loadingTask.promise.then(function (doc) {
console.log("This file has " + doc._pdfInfo.numPages + " pages");
doc.getPage(1).then(page => {
var scale = 1;
var viewPort = page.getViewport(scale);
canvasEl.width = viewPort.width;
canvasEl.height = viewPort.height;
canvasEl.style.width = "100%";
canvasEl.style.height = "100%";
var wrapper = document.getElementById("wrapperDiv");
wrapper.style.width = Math.floor(viewPort.width / scale) + 'px';
wrapper.style.height = Math.floor(viewPort.height / scale) + 'px';
page.render({
canvasContext: ctx,
viewport: viewPort
});
storeCanvas();
});
});
function storeCanvas() {
backupCanvas.width = canvasEl.width;
backupCanvas.height = canvasEl.height;
backupCanvas.ctx = backupCanvas.getContext("2d");
backupCanvas.ctx.drawImage(canvasEl, 0, 0);
}
function restoreCanvas() {
ctx.drawImage(backupCanvas, 0, 0);
}
$canvas.dblclick(function (e) {
var mousePos = getMousePos(canvasEl, e);
var line = { startX: 0, startY: mousePos.Y, endX: canvasEl.width, endY: mousePos.Y, pageY: e.pageY };
lines.push(line);
drawLine(line, lines.length - 1);
});
function drawLine(line, index) {
// draw line
ctx.beginPath();
ctx.strokeStyle = '#df4b26';
ctx.moveTo(line.startX, line.startY);
ctx.lineTo(line.endX, line.endY);
ctx.closePath();
ctx.stroke();
// add remove mark
var top = line.pageY;
var left = canvasEl.width + 20;
var $a = $("<a href='#' class='w-remove-line'>")
.data("line-index", index)
.attr("style", "line-height:0")
.css({ top: top, left: left, position: 'absolute' })
.html("x")
.click(function () {
var index = $(this).data("line-index");
$(".w-remove-line").remove();
ctx.clearRect(0, 0, canvasEl.width, canvasEl.height);
// restore canvas
restoreCanvas();
lines.splice(index, 1);
for (var i = 0; i < lines.length; i++) {
drawLine(lines[i], i);
}
});
$("body").append($a);
}
function getMousePos(canvas, evt) {
var rect = canvas.getBoundingClientRect();
return {
X: Math.floor(evt.clientX - rect.left),
Y: Math.floor(evt.clientY - rect.top),
};
}
});
canvas {
border: 1px solid red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.2.228/pdf.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<b> Double Click on PDF to draw line and then click on X to remove lines</b>
<div id="wrapperDiv">
<canvas id="myCanvas"></canvas>
</div>
The PDF.js render() function is async so you need to store the canvas after the render has finished. Your code is firing storeCanvas() too early and storing a blank canvas. Easy fix, render() returns a promise so ...
page.render({
canvasContext: ctx,
viewport: viewPort
}).then( () => {
storeCanvas();
});
https://jsfiddle.net/fyLant01/1/
Reference: from https://github.com/mozilla/pdf.js/blob/master/src/display/api.js#L998
/**
* Begins the process of rendering a page to the desired context.
* #param {RenderParameters} params Page render parameters.
* #return {RenderTask} An object that contains the promise, which
* is resolved when the page finishes rendering.
*/

Konva.js: Is it possible to animate a path/shape?

See my example: https://jsfiddle.net/ksumarine/x7f9yLb7/4/
I have a .Stage that has an offset which puts 0, 0 at the bottom center of the canvas. This has a .Layer which draws the .Shape of an objects path from an array of x,y coordinates.
let routes = [{x: #, y: #}, {x: #, y: #}, ......] // example routes
let testStage = new Konva.Stage({
container: con,
x: 0,
y: 0,
width: 300,
height: 300,
offsetX: ((300 / 2) * -1),
offsetY: (300 * -1)
});
let testLayer = new Konva.Layer();
let testShape = new Konva.Shape({
sceneFunc: function(context) {
context.beginPath();
for (var i = 0; i < routes.length - 1; i++) {
context.moveTo(routes[i].x, routes[i].y);
context.lineTo(routes[i + 1].x, routes[i + 1].y);
}
context.strokeStyle = '#000';
context.lineWidth = 1.5;
context.stroke();
}
});
testLayer.add(testShape);
testStage.add(testLayer);
//////////// here's the button click event
animateButton.addEventListener('click', e => {
let i = 0;
let _ctx = testLayer.getContext()._context;
let draw = () => {
setTimeout(() => {
if (routes.length) {
_ctx.clearRect(0, 0, 300, 300);
_ctx.beginPath();
_ctx.moveTo(routes[0].x, (routes[0].y * -1));
for (let p = 1, plen = i; p < plen; p++) {
_ctx.lineTo(routes[p].x, (routes[p].y * -1));
}
_ctx.strokeStyle = '#000';
_ctx.lineWidth = 1.5;
_ctx.stroke();
(i === routes.length - 1) ? i = 0: i++;
}
requestAnimationFrame(draw);
}, 1000 / 59.940);
}
draw();
});
Everything at this point works well and the path is drawn as expected. I would like to use Konva.Animation to show how the object moved, but I cannot figure it out or find any examples to help.
I've added normal Canvas JS in the button eventListener as an example of the desired effect, but using the testLayer context to draw with puts 0,0 back at the top left...not what I want.
Could someone point me in the right direction to accomplish this using Konva.Animation?
For those coming across this question it was asked again in Nov 18, and this time there are two answers. See them here

How can I create a new object with multiple methods?

I am trying to follow this tutorial here https://www.smashingmagazine.com/2012/10/design-your-own-mobile-game/ and I am stuck on the second part. (2. A Blank Canvas)
I am not sure where to put the POP.Draw object. Does it go inside of the var POP{} brackets where the other objects are created? I've tried keeping it inside, outside, and in the init function which I don't think makes sense. The purpose is to create methods within the new Draw object so they can be called later to create pictures in the canvas.
Here is my current code. It is the same as the one in the link:
var POP = {
//setting up initial values
WIDTH: 320,
HEIGHT: 480,
// we'll set the rest of these
//in the init function
RATIO: null,
currentWidth: null,
currentHeight: null,
canvas: null,
ctx: null,
init: function() {
//the proportion of width to height
POP.RATIO = POP.WIDTH / POP.HEIGHT;
//these will change when the screen is resized
POP.currentWidth = POP.WIDTH;
POP.currentHeight = POP.HEIGHT;
//this is our canvas element
POP.canvas = document.getElementsByTagName('canvas')[0];
//setting this is important
//otherwise the browser will
//default to 320x200
POP.canvas.width = POP.WIDTH;
POP.canvas.width = POP.HEIGHT;
//the canvas context enables us to
//interact with the canvas api
POP.ctx = POP.canvas.getContext('2d');
//we need to sniff out Android and iOS
// so that we can hide the address bar in
// our resize function
POP.ua = navigator.userAgent.toLowerCase();
POP.android = POP.ua.indexOf('android') > -1 ? true : false;
POP.ios = (POP.ua.indexOf('iphone') > -1 || POP.ua.indexOf('ipad') > -1) ? true : false;
//we're ready to resize
POP.resize();
POP.Draw.clear();
POP.Draw.rect(120, 120, 150, 150, 'green');
POP.Draw.circle(100, 100, 50, 'rgba(225,0,0,0.5)');
POP.Draw.text('Hello WOrld', 100, 100, 10, "#000");
},
resize: function() {
POP.currentHeight = window.innerHeight;
//resize the width in proportion to the new height
POP.currentWidth = POP.currentHeight * POP.RATIO;
//this will create some extra space on the page
//allowing us to scroll past the address bar thus hiding it
if (POP.android || POP.ios) {
document.body.style.height = (window.innerHeight + 50) + 'px';
}
//set the new canvas style width and height note:
//our canvas is still 320 x 400 but we're essentially scaling it with css
POP.canvas.style.width = POP.currentWidth + 'px';
POP.canvas.style.height = POP.currentHeight + 'px';
//we use a timeout here because some mobile browsers
//don't fire if there is not a short delay
window.selfTimeout(function() {
window.scrollTo(0, 1);
})
//this will create some extra space on the page
//enabling us to scroll past the address bar
//thus hiding it
if (POP.android || POP.ios) {
document.body.style.height = (window.innerHeight + 50) + 'px';
}
}
};
window.addEventListener('load', POP.init, false);
window.addEventListener('resize', POP.resize, false);
//abstracts various canvas operations into standalone functions
POP.Draw = {
clear: function() {
POP.ctx.clearRect(0, 0, POP.WIDTH, POP.HEIGHT);
},
rect: function(x, y, w, h, col) {
POP.ctx.fillStyle = col;
POP.ctx.fillRect(x, y, w, h);
},
circle: function(x, y, r, col) {
POP.ctx.fillStyle = col;
POP.ctx.beginPath();
POP.ctx.arc(x + 5, y + 5, r, 0, Math.PI * 2, true);
POP.ctx.closePath();
POP.ctx.fill();
},
text: function(string, x, y, size, col) {
POP.ctx.font = 'bold' + size + 'px Monospace';
POP.ctx.fillStyle = col;
POP.ctx.fillText(string, x, y);
}
};
SOLVED
I didn't realize but the completed code is on the webpage. I downloaded it and looked at the example for answers.
I solved the issue by placing the POP.Draw.clear, POP.Draw.rect methods before calling the POP.resize() method. I'm not really sure why the order matters, but it does.

Kineticjs - free rotate on image

I need help only having the anchors for rotating. Right now there is five anchors and I don't know how to get rid of all of them except the rotate one. I would also only like the anchors to show when the user hovers over the image
Here is my code
<html>
<head>
<style>
body {
margin: 0px;
padding: 0px;
}
</style>
</head>
<body>
<body onmousedown="return false;">
<div id="container"></div>
<script src="http://d3lp1msu2r81bx.cloudfront.net/kjs/js/lib/kinetic-v4.7.4.min.js">
</script>
<script>
function update(activeAnchor) {
var group = activeAnchor.getParent();
var topLeft = group.get('.topLeft')[0];
var topRight = group.get('.topRight')[0];
var bottomRight = group.get('.bottomRight')[0];
var bottomLeft = group.get('.bottomLeft')[0];
var rotateAnchor = group.get('.rotateAnchor')[0];
var image = group.get('Image')[0];
var anchorX = activeAnchor.getX();
var anchorY = activeAnchor.getY();
var imageWidth = image.getWidth();
var imageHeight = image.getHeight();
var offsetX = Math.abs((topLeft.getX() + bottomRight.getX() + 10) / 2);
var offsetY = Math.abs((topLeft.getY() + bottomRight.getY() + 10) / 2);
// update anchor positions
switch (activeAnchor.getName()) {
case 'rotateAnchor':
group.setOffset(offsetX, offsetY);
break;
case 'topLeft':
topRight.setY(anchorY);
bottomLeft.setX(anchorX);
break;
case 'topRight':
topLeft.setY(anchorY);
bottomRight.setX(anchorX);
break;
case 'bottomRight':
topRight.setX(anchorX);
bottomLeft.setY(anchorY);
break;
case 'bottomLeft':
topLeft.setX(anchorX);
bottomRight.setY(anchorY);
break;
}
rotateAnchor.setX(topRight.getX() + 5);
rotateAnchor.setY(topRight.getY() + 20);
image.setPosition((topLeft.getPosition().x + 20), (topLeft.getPosition().y + 20));
var width = topRight.getX() - topLeft.getX() - 30;
var height = bottomLeft.getY() - topLeft.getY() - 30;
if (width && height) {
image.setSize(width, height);
}
}
function addAnchor(group, x, y, name, dragBound) {
var stage = group.getStage();
var layer = group.getLayer();
var anchor = new Kinetic.Circle({
x: x,
y: y,
stroke: '#666',
fill: '#ddd',
strokeWidth: 2,
radius: 8,
name: name,
draggable: true,
dragOnTop: false
});
if (dragBound == 'rotate') {
anchor.setAttrs({
dragBoundFunc: function (pos) {
return getRotatingAnchorBounds(pos, group);
}
});
}
anchor.on('dragmove', function() {
update(this);
layer.draw();
});
anchor.on('mousedown touchstart', function() {
group.setDraggable(false);
this.moveToTop();
});
anchor.on('dragend', function() {
group.setDraggable(true);
layer.draw();
});
// add hover styling
anchor.on('mouseover', function() {
var layer = this.getLayer();
document.body.style.cursor = 'pointer';
this.setStrokeWidth(4);
layer.draw();
});
anchor.on('mouseout', function() {
var layer = this.getLayer();
document.body.style.cursor = 'default';
this.setStrokeWidth(2);
layer.draw();
});
group.add(anchor);
}
function loadImages(sources, callback) {
var images = {};
var loadedImages = 0;
var numImages = 0;
for(var src in sources) {
numImages++;
}
for(var src in sources) {
images[src] = new Image();
images[src].onload = function() {
if(++loadedImages >= numImages) {
callback(images);
}
};
images[src].src = sources[src];
}
}
function getRotatingAnchorBounds(pos, group) {
var topLeft = group.get('.topLeft')[0];
var bottomRight = group.get('.bottomRight')[0];
var topRight = group.get('.topRight')[0];
var absCenterX = Math.abs((topLeft.getAbsolutePosition().x + 5 + bottomRight.getAbsolutePosition().x + 5) / 2);
var absCenterY = Math.abs((topLeft.getAbsolutePosition().y + 5 + bottomRight.getAbsolutePosition().y + 5) / 2);
var relCenterX = Math.abs((topLeft.getX() + bottomRight.getX()) / 2);
var relCenterY = Math.abs((topLeft.getY() + bottomRight.getY()) / 2);
var radius = distance(relCenterX, relCenterY, topRight.getX() + 5, topRight.getY() + 20);
var scale = radius / distance(pos.x, pos.y, absCenterX, absCenterY);
var realRotation = Math.round(degrees(angle(relCenterX, relCenterY, topRight.getX() + 5, topRight.getY() + 20)));
var rotation = Math.round(degrees(angle(absCenterX, absCenterY, pos.x, pos.y)));
rotation -= realRotation;
group.setRotationDeg(rotation);
return {
y: Math.round((pos.y - absCenterY) * scale + absCenterY),
x: Math.round((pos.x - absCenterX) * scale + absCenterX)
};
}
function radians(degrees) { return degrees * (Math.PI / 180); }
function degrees(radians) { return radians * (180 / Math.PI); }
// Calculate the angle between two points.
function angle(cx, cy, px, py) {
var x = cx - px;
var y = cy - py;
return Math.atan2(-y, -x);
}
// Calculate the distance between two points.
function distance(p1x, p1y, p2x, p2y) {
return Math.sqrt(Math.pow((p2x - p1x), 2) + Math.pow((p2y - p1y), 2));
}
function initStage(images) {
var stage = new Kinetic.Stage({
container: 'container',
width: 578,
height: 400
});
var darthVaderGroup = new Kinetic.Group({
x: 270,
y: 100,
draggable: true
});
var yodaGroup = new Kinetic.Group({
x: 100,
y: 110,
draggable: true
});
var layer = new Kinetic.Layer();
/*
* go ahead and add the groups
* to the layer and the layer to the
* stage so that the groups have knowledge
* of its layer and stage
*/
layer.add(darthVaderGroup);
layer.add(yodaGroup);
stage.add(layer);
// darth vader
var darthVaderImg = new Kinetic.Image({
x: 0,
y: 0,
image: images.darthVader,
width: 200,
height: 138,
name: 'image'
});
darthVaderGroup.add(darthVaderImg);
addAnchor(darthVaderGroup, -20, -20, 'topLeft', 'none');
addAnchor(darthVaderGroup, 220, -20, 'topRight', 'none');
addAnchor(darthVaderGroup, 220, 158, 'bottomRight', 'none');
addAnchor(darthVaderGroup, -20, 158, 'bottomLeft','none');
addAnchor(darthVaderGroup, 225, 0, 'rotateAnchor','rotate');
darthVaderGroup.on('dragstart', function() {
this.moveToTop();
});
stage.draw();
}
var sources = {
darthVader: 'http://www.html5canvastutorials.com/demos/assets/darth-vader.jpg'
};
loadImages(sources, initStage);
</script>
</body>
</html>
You can use each anchors show/hide methods inside the images mouseenter/mouseleave events to display the anchors when the mouse enters the image:
image.on("mouseleave",function(){ anchor1.hide(); }
image.on("mouseenter",function(){ anchor1.show(); layer.draw(); }
Problem is that since your anchors are partly outside your image, so hiding the anchors when the mouse leaves the image might make the anchors "disappear" when the user intends to use them.
The ideal solution would be to listen for mouseenter/mouseleave events on the group which contains the image but also extends to include the outside part of the anchors. Unfortunately, a Kinetic.Group will not respond to mouseenter/mouseleave events.
A workaround is to create a Kinetic.Rect background to the group which includes the images plus the anchors. The rect will listen for mouseenter/mouseleave events and will show/hide the anchors. If you don't want the background rect to be visible, just set it's opacity to .001. The rect will still listen for events, but will be invisible.
groupBackgroundRect.on("mouseleave",function(){ anchor1.hide(); }
groupBackgroundRect.on("mouseenter",function(){ anchor1.show(); layer.draw(); }
A related note:
With KineticJS, combining rotation with resizing is made more difficult than it needs to be because KineticJS uses offsetX/offsetY as both an object's rotation point and as an offset to its position. Your key to making it work will be to re-center the offset point after resizing so that your rotation takes place around the new centerpoint--not the previous centerpoint. (or reset the offset reference point to any other point that you want to rotate around).

Categories

Resources