<!DOCTYPE html>
<html>
<head>
<title>Page Title</title>
<style>
body {
font-family: "Lato", sans-serif;
}
.sidenav {
height: 100%;
width: 0;
position: fixed;
z-index: 1;
top: 0;
left: 0;
background-color: #111;
overflow-x: hidden;
transition: 0.5s;
padding-top: 60px;
}
.sidenav a {
padding: 8px 8px 8px 32px;
text-decoration: none;
font-size: 25px;
color: #818181;
display: block;
transition: 0.3s;
}
.sidenav a:hover {
color: #f1f1f1;
}
.sidenav .closebtn {
position: absolute;
top: 0;
right: 25px;
font-size: 36px;
margin-left: 50px;
}
#main {
transition: margin-left .5s;
padding: 16px;
}
#media screen and (max-height: 450px) {
.sidenav {padding-top: 15px;}
.sidenav a {font-size: 18px;}
}
</style>
</head>
<body>
<div id="mySidenav" class="sidenav">
×
About
Services
Clients
Contact
</div>
<div id="main">
<span style="font-size:30px;cursor:pointer" onclick="openNav()">☰ Menu</span>
<canvas id="canvas" width="600" height="350" style="border:2px solid #d3d3d3;">></canvas>
</div>
<script>
var zoomIntensity = 0.1;
var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");
var width = 90;
var height = 50;
var scale = 1;
var originx = 0;
var originy = 0;
var visibleWidth = width;
var visibleHeight = height;
var c = document.getElementById("canvas");
var ctx = c.getContext("2d");
function draw(){
context.fillStyle = "white";
context.fillRect(originx,originy,1000/scale,800/scale);
context.fillStyle = "blue";
context.fillRect(170,50,50,50);
context.fillStyle = "white";
context.fillText('click here',175,70);
}
setInterval(draw, 1000/60);
canvas.onwheel = function (event){
event.preventDefault();
var mousex = event.clientX - canvas.offsetLeft;
var mousey = event.clientY - canvas.offsetTop;
var wheel = event.deltaY < 0 ? 1 : -1;
var zoom = Math.exp(wheel*zoomIntensity);
context.translate(originx, originy);
originx -= mousex/(scale*zoom) - mousex/scale;
originy -= mousey/(scale*zoom) - mousey/scale;
context.scale(zoom, zoom);
context.translate(-originx, -originy);
scale *= zoom;
visibleWidth = width / scale;
visibleHeight = height / scale;
}
function openNav() {
document.getElementById("mySidenav").style.width = "200px";
document.getElementById("main").style.marginLeft = "200px";
}
function closeNav() {
document.getElementById("mySidenav").style.width = "0";
document.getElementById("main").style.marginLeft= "0";
}
</script>
</body>
</html>
is there away to make a shape or object clickable on canvas frame or viewbox, even when i zoom in or out, cause all the examples i saw was just a fixed clickable location. for instance google map locations when i zoom in i can click more objects.
is there away to make a shape or object clickable on canvas frame or viewbox, even when i zoom in or out, cause all the examples i saw was just a fixed clickable location. for instance google map locations when i zoom in i can click more objects.
There are two spaces you have to account for, the actual canvas space and the translated space. This means you need to translate the mouse coordinates in order to know where in the space the object is actually located and map it back to the canvas coordinates so you can know if you are clicking on it. This is achieved by keeping track of the offset value you provided in the var originx, but the problem is that there is no way based on my trails at least for you to translate the mouse click location if you use the context.translate(originx, originy) if you both scale and pan the object space.
I have rewritten the code without the use of the translate function by making my own translate function that enables you to translate between the canvas space and object space in order to register a click event on the object regardless of the panned or zoomed in location.
This function also has a click to pan feature so you can click and drag the object screen to where ever you want to object to be positioned.
/*jshint esversion: 8 */
(function() {
const canvas = document.querySelector("canvas");
const context = canvas.getContext("2d");
let canvasWidth = 600;
let canvasHeight = 600;
let offset = {
x: 0,
y: 0
};
let pan = {
x: 0,
y: 0
};
var zoomIntensity = 0.1;
let scale = 1;
let mouse = {
x: 0,
y: 0
};
let dragging = false;
let square;
let shapes = [{
x: 170,
y: 50,
w: 50,
h: 50,
color: "blue"
},
{
x: 85,
y: 25,
w: 50,
h: 50,
color: "red"
},
{
x: 50,
y: 100,
w: 50,
h: 50,
color: "green"
},
];
function init() {
canvas.width = canvasWidth;
canvas.height = canvasHeight;
renderCanvas();
}
function drawSquare(x, y, width, height, color) {
context.fillStyle = color;
context.fillRect(x, y, width, height);
}
function objectsToCanvasScreen(x, y) {
const canvasScreenX = Math.floor((x - offset.x) * scale);
const canvasScreenY = Math.floor((y - offset.y) * scale);
return {
x: canvasScreenX,
y: canvasScreenY
};
}
function canvasToObjectsScreen(x, y) {
return {
x: x / scale + offset.x,
y: y / scale + offset.y
};
}
function renderCanvas() {
context.clearRect(0, 0, canvas.width, canvas.height);
shapes.forEach(({
x,
y,
w,
h,
color
}, index) => {
const {
x: csx,
y: csy
} = objectsToCanvasScreen(x, y);
shapes[index]._x = csx;
shapes[index]._y = csy;
shapes[index]._w = w * scale;
shapes[index]._h = h * scale;
drawSquare(csx, csy, w * scale, h * scale, color);
});
}
canvas.onwheel = function(e) {
const zoom = Math.exp((e.deltaY < 0 ? 1 : -1) * zoomIntensity);
const beforeZoom = canvasToObjectsScreen(mouse.x, mouse.y);
scale *= zoom;
const afterZoom = canvasToObjectsScreen(mouse.x, mouse.y);
offset.x += beforeZoom.x - afterZoom.x;
offset.y += beforeZoom.y - afterZoom.y;
renderCanvas();
};
canvas.onmousedown = function(e) {
if (e.button === 0) {
pan.x = mouse.x;
pan.y = mouse.y;
dragging = true;
}
};
canvas.onmouseup = (e) => (dragging = false);
canvas.onmousemove = function(e) {
mouse.x = e.offsetX;
mouse.y = e.offsetY;
if (dragging) {
offset.x -= (mouse.x - pan.x) / scale;
offset.y -= (mouse.y - pan.y) / scale;
pan.x = mouse.x;
pan.y = mouse.y;
renderCanvas();
}
};
canvas.onclick = function(e) {
shapes.forEach(({
_x,
_y,
_w,
_h,
color
}) => {
const {
x: mousex,
y: mousey
} = canvasToObjectsScreen(mouse.x, mouse.y);
const {
x: squarex,
y: squarey
} = canvasToObjectsScreen(_x, _y);
if (
mousex >= squarex &&
mousex <= _w / scale + squarex &&
mousey >= squarey &&
mousey <= _h / scale + squarey
) {
alert(`${color} clicked!`);
}
});
};
init();
})();
body {
font-family: "Lato", sans-serif;
}
#canvas {
background: #E1BC8B;
}
<body>
<div id="main">
<canvas id="canvas" width="300" height="350"></canvas>
</div>
</body>
Related
Here below is how to draw a "selection rectangle" on a <canvas> with drag-and-drop, see How to draw a selection rectangle with drag and drop on a HTML canvas?.
Is there a simple way to detect the selection rectangle on hover at a distance of a few pixels, and allow to move the selection rectangle with drag-and-drop?
var c1 = document.getElementById("c1"), c2 = document.getElementById("c2");
var ctx1 = c1.getContext("2d"), ctx2 = c2.getContext("2d");
ctx2.setLineDash([5, 5]);
var origin = null;
window.onload = () => { ctx1.drawImage(document.getElementById("img"), 0, 0); }
c2.onmousedown = e => { origin = {x: e.offsetX, y: e.offsetY}; };
window.onmouseup = e => { origin = null; };
c2.onmousemove = e => {
if (!!origin) {
ctx2.strokeStyle = "#ff0000";
ctx2.clearRect(0, 0, c2.width, c2.height);
ctx2.beginPath();
ctx2.rect(origin.x, origin.y, e.offsetX - origin.x, e.offsetY - origin.y);
ctx2.stroke();
}
};
#img { display: none; }
#canvas-container { position: relative; }
canvas { position: absolute; left: 0; top: 0; }
#c1 { z-index: 0; }
#c2 { z-index: 1; }
<img id="img" src="https://i.imgur.com/okSIKkW.jpg">
<div id="canvas-container">
<canvas id="c1" height="200" width="200"></canvas>
<canvas id="c2" height="200" width="200"></canvas>
</div>
Storing the current selection
To be able to move an existing selection, you have to save its state. Right now, your code "forgets" about it after drawing it once.
You can save your selection in a variable on mouseup like so:
const dx = origin.x - mouse.x;
const dy = origin.y - mouse.y;
selection = {
left: Math.min(mouse.x, origin.x),
top: Math.min(mouse.y, origin.y),
width: Math.abs(dx),
height: Math.abs(dy)
}
Intersection check
Your selection is a rectangle. You can check whether the mouse is intersecting the rectangle like so:
const intersects = (
mouse.x >= selection.left &&
mouse.x <= selection.left + selection.width &&
mouse.y >= selection.top &&
mouse.y <= selection.top + selection.height
);
If you want to add some padding around the rectangle, you can change the checks to be, for example, mouse.x >= selection.left - PADDING.
Make or Move
You now have to support 2 types of interactions:
Making new selections, and
Moving existing selections
This is where my implementation gets a bit messy, but you can probably refactor it yourself 🙂
I didn't change much in the making-selections code, other than saving the selection to a variable.
When moving selections, you take the dx and dy of your mouse drag and add them to the selection's original position (ox, oy):
const dx = origin.x - mouse.x;
const dy = origin.y - mouse.y;
selection.left = ox - dx;
selection.top = oy - dy;
Here's everything in a runnable snippet:
var c1 = document.getElementById("c1"),
c2 = document.getElementById("c2");
var ctx1 = c1.getContext("2d"),
ctx2 = c2.getContext("2d");
ctx2.setLineDash([5, 5]);
var origin = null;
let selection = null;
let selectionHovered = false;
let interaction = null;
let ox = null;
let oy = null;
window.onload = () => { ctx1.drawImage(document.getElementById("img"), 0, 0); }
c2.onmousedown = e => {
origin = {x: e.offsetX, y: e.offsetY};
if (selectionHovered) {
interaction = "MOVE_SELECTION";
ox = selection.left;
oy = selection.top;
} else {
interaction = "MAKE_SELECTION";
}
};
window.onmouseup = e => {
interaction = null;
origin = null;
};
c2.onmousemove = e => {
const x = e.offsetX;
const y = e.offsetY;
if (!interaction) {
selectionHovered = (
selection &&
x >= selection.left &&
x <= selection.left + selection.width &&
y >= selection.top &&
y <= selection.top + selection.height
);
} else {
const dx = origin.x - x;
const dy = origin.y - y;
// Update
switch (interaction) {
case "MOVE_SELECTION":
selection.left = ox - dx;
selection.top = oy - dy;
break;
case "MAKE_SELECTION":
selection = {
left: Math.min(x, origin.x),
top: Math.min(y, origin.y),
width: Math.abs(dx),
height: Math.abs(dy)
}
break
default:
// Do nothing
}
// Set selectionHovered
if (selection) {
} else {
selectionHovered = false;
}
}
// Draw
if (selection) drawSelection(selection);
};
function drawSelection({ top, left, width, height }) {
// Draw rect
ctx2.strokeStyle = "#ff0000";
ctx2.clearRect(0, 0, c2.width, c2.height);
ctx2.beginPath();
ctx2.rect(left, top, width, height);
ctx2.stroke();
// Set mouse
c2.style.cursor = selectionHovered ? "move" : "default";
}
#img { display: none; }
body { margin: 0; }
#canvas-container { position: relative; }
canvas { position: absolute; left: 0; top: 0; }
#c1 { z-index: 0; }
#c2 { z-index: 1; }
<img id="img" src="https://i.imgur.com/okSIKkW.jpg">
<div id="canvas-container">
<canvas id="c1" height="200" width="200"></canvas>
<canvas id="c2" height="200" width="200"></canvas>
</div>
I am trying to make an object move to click position, like in rpgs like diablo or modern moba games, i needed to find a way to animate to click coordinates and i have found a method that uses pythagorean theorem, i understood and custimized the code to a certain degree, but there is a bug where the ball keeps bouncing in the very end of animation. I know this happens because of the "moves" variable and the loop, but can't understand exactly why.
here's the function that draws the object
function drawPlayer() {
//
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.beginPath();
ctx.arc(player.x, player.y, 12, 0, Math.PI*2);
ctx.fillStyle = 'white';
ctx.fill();
ctx.closePath();
//Calculate variables
let dx = player.newx - player.x;
let dy = player.newy - player.y;
let distance = Math.sqrt(dx*dx + dy*dy);
let moves = distance/player.speed;
let xunits = (player.newx - player.x)/moves;
let yunits = (player.newy - player.y)/moves;
if (moves > 0 ) {
moves--;
player.x += xunits;
player.y += yunits;
console.log(moves);
}
}
const canvas = document.getElementById('canvas1');
const ctx = canvas.getContext('2d');
canvas.addEventListener('click', e => {
setClickCoords(e);
drawPlayer();
})
canvas.width = 300;
canvas.height = 300;
const player = {
x: 0,
y: 0,
newx: 0,
newy: 0,
speed: 1,
radius: 15,
}
function drawPlayer() {
//Draw
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.beginPath();
ctx.arc(player.x, player.y, 12, 0, Math.PI * 2);
ctx.fillStyle = 'white';
ctx.fill();
ctx.closePath();
//Calculate variables
let dx = player.newx - player.x;
let dy = player.newy - player.y;
let distance = Math.sqrt(dx * dx + dy * dy);
let moves = distance / player.speed;
let xunits = (player.newx - player.x) / moves;
let yunits = (player.newy - player.y) / moves;
console.log(moves);
if (moves > 0) {
moves--;
player.x += xunits;
player.y += yunits;
console.log(moves);
}
}
function setClickCoords(e) {
player.newx = e.offsetX;
player.newy = e.offsetY;
document.getElementById('oldcoords').innerHTML = "old Coords " + player.x + " " + player.y;
document.getElementById('newcoords').innerHTML = "new Coords " + player.newx + " " + player.newy;
}
function gameLoop() {
window.setTimeout(gameLoop, 24);
drawPlayer()
}
gameLoop();
body {
background: black;
display: flex;
flex-direction: column;
}
#canvas1 {
border: 3px solid white;
top: 50%;
left: 50%;
position: absolute;
width: 300px;
height: 300px;
transform: translate(-50%, -50%);
}
#oldcoords,
#newcoords {
color: white;
font-size: 18px;
}
<span id="oldcoords"></span>
<span id="newcoords"></span>
<canvas id="canvas1"> </canvas>
Check if moves is between 0 and 1. If it's between, move the ball to the desired position. Currently, the ball goes too far in the direction and has to return in the next animation.
const canvas = document.getElementById('canvas1');
const ctx = canvas.getContext('2d');
canvas.addEventListener('click', e => {
setClickCoords(e);
drawPlayer();
})
canvas.width = 300;
canvas.height = 300;
const player = {
x: 0,
y: 0,
newx: 0,
newy: 0,
speed: 1,
radius: 15,
}
function drawPlayer() {
//Draw
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.beginPath();
ctx.arc(player.x, player.y, 12, 0, Math.PI * 2);
ctx.fillStyle = 'white';
ctx.fill();
ctx.closePath();
//Calculate variables
let dx = player.newx - player.x;
let dy = player.newy - player.y;
let distance = Math.sqrt(dx * dx + dy * dy);
let moves = distance / player.speed;
console.log(moves);
if (moves > 1) {
let xunits = (player.newx - player.x) / moves;
let yunits = (player.newy - player.y) / moves;
moves--;
player.x += xunits;
player.y += yunits;
console.log(moves);
} else if (moves > 0) {
moves = 0;
player.x = player.newx;
player.y = player.newy;
console.log(moves);
}
}
function setClickCoords(e) {
player.newx = e.offsetX;
player.newy = e.offsetY;
document.getElementById('oldcoords').innerHTML = "old Coords " + player.x + " " + player.y;
document.getElementById('newcoords').innerHTML = "new Coords " + player.newx + " " + player.newy;
}
function gameLoop() {
window.setTimeout(gameLoop, 24);
drawPlayer()
}
gameLoop();
body {
background: black;
display: flex;
flex-direction: column;
}
#canvas1 {
border: 3px solid white;
top: 50%;
left: 50%;
position: absolute;
width: 300px;
height: 300px;
transform: translate(-50%, -50%);
}
#oldcoords,
#newcoords {
color: white;
font-size: 18px;
}
<span id="oldcoords"></span>
<span id="newcoords"></span>
<canvas id="canvas1"> </canvas>
I have a canvas demo that spawns some circles. I can't seem to figure out how to have it be responsive in setting either canvas.width or canvas.style.width (what's the difference here?) on a windows resize using window.innerWidth.
Code works fine but it rerenders strangely on smaller viewports. I tried adding this snippet of code at the bottom of my javascript file, but it broke animation.
// ADDING THESE LINES PREVENTS ANIMATION FROM RUNNING
if (window.innerWidth < 1000) {
canvas.width = window.innerWidth;
} else {
canvas.width = 1000;
}
var canvas = document.querySelector("canvas");
canvas.width = 1000;
canvas.height = 100;
var c = canvas.getContext("2d");
// Constructor Function (object blueprint)
function Circle(x, y, dx, dy, radius, counter) {
this.x = x;
this.y = y;
this.dx = dx;
this.dy = dy;
this.radius = radius;
this.counter = counter;
this.draw = function() {
c.beginPath();
c.arc(this.x, this.y, this.radius, 0, Math.PI * 2, false);
c.strokeStyle = "white";
c.stroke();
c.fillStyle = "white";
c.fill();
};
this.update = function() {
if (this.y + this.radius > canvas.height) {
this.y = 0;
}
this.x += this.dx;
this.y += this.dy;
this.draw();
};
}
// Initialize array to store snow objects
var circleArray = [];
// Initialize objects with constructor
for (var i = 0; i < 50; i++) {
var radius = 1 + Math.random() * 5;
var x = Math.random() * canvas.width;
var y = 0 - Math.random() * 50; // start at top, render some circles off screen
var dx = (Math.random() - 0.5) * 2;
var dy = 0.5 + Math.random() * 0.5; // use gravity
circleArray.push(new Circle(x, y, dx, dy, radius, 0));
}
function animate() {
requestAnimationFrame(animate); // recurisvely run
c.clearRect(0, 0, innerWidth, innerHeight); // erases previously drawn content
for (var i = 0; i < circleArray.length; i++) {
circleArray[i].update();
}
// ADDING THESE LINES PREVENTS ANIMATION FROM RUNNING
//if (window.innerWidth < 1000) {
// canvas.width = window.innerWidth;
// } else {
// canvas.width = 1000;
// }
}
animate();
body {
background-color: grey;
display: flex;
justify-content: center;
}
.wrapper {
position: relative;
width: 1000px;
height: 110px;
min-height: 110px;
margin-top: 50vh;
}
.wrapper > * {
width: 1000px;
position: absolute;
}
canvas {
position: absolute;
}
img {
width: 100%;
height: 110px;
}
<div class="wrapper">
<img class="stars" src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/867725/stars.png
" alt="" />
<img class="tress" src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/867725/trees.png
" alt="" />
<img class="clouds" src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/867725/clouds.png" alt="" />
<canvas></canvas>
</div>
The difference between canvas.width and canvas.style.width is that canvas.width specifies the actual size of the canvas in pixels, while canvas.style.width stretches or compresses the canvas to the width you specify. This will set the canvas width to 300px:
canvas.width = 300;
This will also set the canvas width to 300px, but will stretch it until it reaches 600px:
canvas.width = 300;
canvas.style.width = "600px";
Using canvas.width is better practice.
For the animation to run you must first fix the canvas width. Putting the code that resizes the canvas at the beginning of the animate() function solves the problem:
var canvas = document.querySelector("canvas");
canvas.width = 1000;
canvas.height = 100;
var c = canvas.getContext("2d");
// Constructor Function (object blueprint)
function Circle(x, y, dx, dy, radius, counter) {
this.x = x;
this.y = y;
this.dx = dx;
this.dy = dy;
this.radius = radius;
this.counter = counter;
this.draw = function() {
c.beginPath();
c.arc(this.x, this.y, this.radius, 0, Math.PI * 2, false);
c.strokeStyle = "white";
c.stroke();
c.fillStyle = "white";
c.fill();
};
this.update = function() {
if (this.y + this.radius > canvas.height) {
this.y = 0;
}
this.x += this.dx;
this.y += this.dy;
this.draw();
};
}
// Initialize array to store snow objects
var circleArray = [];
// Initialize objects with constructor
for (var i = 0; i < 50; i++) {
var radius = 1 + Math.random() * 5;
var x = Math.random() * canvas.width;
var y = 0 - Math.random() * 50; // start at top, render some circles off screen
var dx = (Math.random() - 0.5) * 2;
var dy = 0.5 + Math.random() * 0.5; // use gravity
circleArray.push(new Circle(x, y, dx, dy, radius, 0));
}
function animate() {
if (window.innerWidth < 1000) {
canvas.width = window.innerWidth;
} else {
canvas.width = 1000;
}
requestAnimationFrame(animate); // recurisvely run
c.clearRect(0, 0, innerWidth, innerHeight); // erases previously drawn content
for (var i = 0; i < circleArray.length; i++) {
circleArray[i].update();
}
}
animate();
body {
background-color: grey;
display: flex;
justify-content: center;
}
.wrapper {
position: relative;
width: 1000px;
height: 110px;
min-height: 110px;
margin-top: 50vh;
}
.wrapper>* {
width: 1000px;
position: absolute;
}
canvas {
position: absolute;
}
img {
width: 100%;
height: 110px;
}
<div class="wrapper">
<img class="stars" src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/867725/stars.png
" alt="" />
<img class="tress" src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/867725/trees.png
" alt="" />
<img class="clouds" src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/867725/clouds.png" alt="" />
<canvas></canvas>
</div>
I made a blurred banner which on mouse move became clear. For this I used 2 images, one blurred and one clear. I want to make the blur from css and use one single image but then when I put filter on this image the banner doesn't become clear on mouse move. Can anyone help me? Thanks! I tried many manners to put this filter but none works correctly.I need to use jpg or png not svg because theese are the formats that clients give me
var image = document.querySelector('.zzz img');
var imageCanvas = document.createElement('canvas');
var imageCanvasContext = imageCanvas.getContext('2d');
var lineCanvas = document.createElement('canvas');
var lineCanvasContext = lineCanvas.getContext('2d');
var pointLifetime = 9999999999999999999999999999;
var points = [];
if (image.complete) {
start();
} else {
image.onload = start;
}
/**
* Attaches event listeners and starts the effect.
*/
function start() {
document.addEventListener('mousemove', onMouseMove);
window.addEventListener('resize', resizeCanvases);
var xxx=document.querySelector('.banner');
xxx.appendChild(imageCanvas);
resizeCanvases();
tick();
}
/**
* Records the user's cursor position.
*
* #param {!MouseEvent} event
*/
function onMouseMove(event) {
var rect = document.querySelector('.banner').getBoundingClientRect();
points.push({
time: Date.now(),
x: event.clientX-rect.left,
y: event.clientY-rect.top
});
}
/**
* Resizes both canvases to fill the window.
*/
function resizeCanvases() {
imageCanvas.width = lineCanvas.width = 300;
imageCanvas.height = lineCanvas.height = 250;
}
/**
* The main loop, called at ~60hz.
*/
function tick() {
// Remove old points
points = points.filter(function(point) {
var age = Date.now() - point.time;
return age < pointLifetime;
});
drawLineCanvas();
drawImageCanvas();
requestAnimationFrame(tick);
}
/**
* Draws a line using the recorded cursor positions.
*
* This line is used to mask the original image.
*/
function drawLineCanvas() {
var minimumLineWidth = 25;
var maximumLineWidth = 100;
var lineWidthRange = maximumLineWidth - minimumLineWidth;
var maximumSpeed = 50;
lineCanvasContext.clearRect(0, 0, lineCanvas.width, lineCanvas.height);
lineCanvasContext.lineCap = 'round';
lineCanvasContext.shadowBlur = 30;
lineCanvasContext.shadowColor = '#000';
for (var i = 1; i < points.length; i++) {
var point = points[i];
var previousPoint = points[i - 1];
// Change line width based on speed
var distance = getDistanceBetween(point, previousPoint);
var speed = Math.max(0, Math.min(maximumSpeed, distance));
var percentageLineWidth = (maximumSpeed - speed) / maximumSpeed;
lineCanvasContext.lineWidth = minimumLineWidth + percentageLineWidth * lineWidthRange;
// Fade points as they age
var age = Date.now() - point.time;
var opacity = (pointLifetime - age) / pointLifetime;
lineCanvasContext.strokeStyle = 'rgba(0, 0, 0, ' + opacity + ')';
lineCanvasContext.beginPath();
lineCanvasContext.moveTo(previousPoint.x, previousPoint.y);
lineCanvasContext.lineTo(point.x, point.y);
lineCanvasContext.stroke();
}
}
/**
* #param {{x: number, y: number}} a
* #param {{x: number, y: number}} b
* #return {number} The distance between points a and b
*/
function getDistanceBetween(a, b) {
return Math.sqrt(Math.pow(a.x - b.x, 2) + Math.pow(a.y - b.y, 2));
}
/**
* Draws the original image, masked by the line drawn in drawLineToCanvas.
*/
function drawImageCanvas() {
// Emulate background-size: cover
var width = 300;
var height = 250;
if (height < imageCanvas.height) {
width = imageCanvas.height / image.naturalHeight * image.naturalWidth;
height = imageCanvas.height;
}
imageCanvasContext.clearRect(0, 0, imageCanvas.width, imageCanvas.height);
imageCanvasContext.globalCompositeOperation = 'source-over';
imageCanvasContext.drawImage(image, 0, 0, width, height);
imageCanvasContext.globalCompositeOperation = 'destination-in';
imageCanvasContext.drawImage(lineCanvas, 0, 0);
}
.zzz img {
display: none;
}
.blur img{
filter:blur(5px);
}
.zzz{
position: absolute;
z-index:200;
}
.banner{
width: 300px;
height: 250px;
overflow: hidden;
position: relative;
margin:0 auto 0;
background-image: url('http://www.dbdesign.ro/blur/a_blur.png');
/* filter: blur(5px);
-webkit-filter: blur(5px);*/
/* background-image: filter(url(a.png), blur(20px));*/
/* filter: blur(5px);
-webkit-filter: blur(5px);*/
}
canvas{
position: absolute;
z-index:999;
}
<div class="banner" style="">
<div class="zzz"><img src="http://www.dbdesign.ro/blur/a.png"></div>
</div>
A simple way still using the canvas API, is to use a CanvasPattern from your original image, and only fill where your mouse passed.
Now, you can position your canvas over your CSS filtered <img> and it will cover it with the clear image:
img.onload = function() {
canvas.width = this.width;
canvas.height = this.height;
var rad = 30;
var ctx = canvas.getContext('2d');
ctx.fillStyle = ctx.createPattern(img, 'no-repeat');
canvas.onmousemove = handlemousemove;
function handlemousemove(evt) {
var x = evt.pageX - canvas.offsetLeft;
var y = evt.pageY - canvas.offsetTop;
draw(x, y);
}
function draw(x, y) {
ctx.beginPath();
ctx.moveTo(x + rad, y);
ctx.arc(x, y, rad, 0, Math.PI*2);
ctx.fill();
}
};
#banner{
position: relative;
}
#banner img{
filter: blur(5px);
}
#banner canvas{
position: absolute;
left: 0;
top: 0;
}
<div id="banner">
<img id="img" src="https://upload.wikimedia.org/wikipedia/commons/5/55/John_William_Waterhouse_A_Mermaid.jpg">
<canvas id="canvas"></canvas>
</div>
$(document).ready(function(){
canvas = document.getElementById('canvas');
ctx = canvas.getContext('2d'),
img = new Image;
img.onload = draw;
img.src = 'http://fariskassim.com/stage/strip/v3/img/before1.jpg';
function draw() {
//drawImageProp(ctx, this, 0, 0, canvas.width, canvas.height);
drawImageProp(ctx, this, 0, 0, canvas.width, canvas.height, 0.1, 0.5);
}
ERASE_W=150;
ERASE_H=70;
rzr=$("#razor2");
RAZOR_W=rzr.width();
RAZOR_H=rzr.height();
/**
* By Ken Fyrstenberg Nilsen
*
* drawImageProp(context, image [, x, y, width, height [,offsetX, offsetY]])
*
* If image and context are only arguments rectangle will equal canvas
*/
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 (nh < h) ar = h / nh;
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);
}
});
var hand = $('#razor2')[0];
(function() {
var origin = {
x: $(window).height(),
y: $(window).width()
}
window.onmousemove = handleMouseMove;
function handleMouseMove(event) {
event = event || window.event; // IE-ism
// event.clientX and event.clientY contain the mouse position
hand.style.left = event.clientX-92+"px";
hand.style.top = event.clientY-5+"px";
var leftSide = false;
var d = {
x: origin.x - event.clientX,
y: origin.y - event.clientY
};
var angle = Math.atan2(d.x, d.y) * 90 / Math.PI * -1;
if (leftSide) {
//angle = angle * -1;
}
hand.style["-webkit-transform"] = "rotate("+parseInt(angle,10)+"deg)";
hand.style["transform"] = "rotate("+parseInt(angle,10)+"deg)";
}
})();
function erase(e){
var cvxw = ctx.canvas.width;
var parentOffset = $("#razor2").parent().offset();
//or $(this).offset(); if you really just want the current element's offset
var relX = e.pageX + 0;
var relY = e.pageY + 0;
ctx.clearRect((relX-(RAZOR_W/2)),(relY-(RAZOR_H/2)),ERASE_W,ERASE_H);
}
function draw_razor(e){
var parentOffset = $("#razor2").parent().offset();
//or $(this).offset(); if you really just want the current element's offset
var relX = e.pageX - parentOffset.left;
var relY = e.pageY - parentOffset.top;
rzr.show();
rzr.css('margin-top','-'+(RAZOR_H/2)+'px');
rzr.css('margin-left','-'+(RAZOR_W/2)+'px');
rzr.css('left',relX+'px');
rzr.css('top',relY+'px');
};
$(document).mousemove(function(e){
draw_razor(e);
});
$('#razor2').bind('mousedown', function(e){
$('#razor2').bind('mousemove', function(e){
erase(e);
hand.className = "active";
});
$('#razor2').bind('mouseup',function(){
$('#razor2').unbind('mousemove')
hand.className = "";
});
});
$('#razor2').on('dragstart', function(e) {
e.preventDefault();
});
* {
padding: 0;
margin: 0;
}
#game {
position: relative;
display: block;
width: 100vw;
font-size: 0;
-webkit-box-shadow: 3px 3px 7px 1px rgba(0, 0, 0, 0.8);
box-shadow: 3px 3px 7px 1px rgba(0, 0, 0, 0.8);
margin:0;
overflow:hidden;
}
body {
background-color: navajo;
background-position: center top;
background-repeat: no-repeat;
margin:0;
}
.canvas_wrap {
width: 100%;
height: 0;
padding-bottom: 50%;
position:relative;
overflow: hidden;
}
canvas {
/*cursor: none;*/
position: absolute;
background: transparent;
top:0;
left: 0;
z-index: 3;
width:100%;
height: 100%;
object-fit: cover;
}
#kitten{
width: 100%;
height: 100%;
object-fit: cover;
position:absolute;
top:0;
left: 0;
z-index: 2;
}
#razor2 {
background-image:url(http://fariskassim.com/stage/strip/v3/img/razor.png);
/*cursor: none;*/
position: absolute;
z-index: 98;
width: 800px;
height: 476px;
margin-top: -200px !important;
margin-left: -200px !important;
background-size: cover;
object-position: -99999px 99999px;
}
#razor2.active {
background-position: 0 100% !important;
}
#razor {
height: 0;
width: 0;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="game">
<img id="razor" src="img/razor.png">
<div id="razor2"></div>
<div class="canvas_wrap">
<canvas id="canvas" width="1730" height="870">Los ti browser!</canvas>
<img id="kitten" src="http://fariskassim.com/stage/strip/v3/img/after1.jpg">
</div>
</div>
As you'll see, when you start dragging your mouse across the canvas, you'll see that the clearRect (shaving hair) is not taking place on the mouse position. its a little off to the left
i believe this is because i scaled the canvas to make it responsive.
Anyone knows how i modify my code to make the clearRect work on the position of the mouse?