Image following a mouse on a canvas - javascript

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>

Related

How to draw behind an image in canvas?

I am using canvas for a picture crop feature and I cannot figure out how to fill the empty spaces.
First, this is what I'm doing:
Canvas element:
<canvas
id="previewCanvas"
ref={previewCanvasRef}
style={{
border: '1px solid black',
objectFit: 'contain',
width: '300px',
height: '300px',
}}
/>
This is the function I use to draw the image:
const TO_RADIANS = Math.PI / 180;
export async function canvasPreview(image, canvas, crop, scale = 1, rotate = 0) {
const ctx = canvas.getContext('2d');
if (!ctx) {
throw new Error('No 2d context');
}
const scaleX = image.naturalWidth / image.width;
const scaleY = image.naturalHeight / image.height;
// devicePixelRatio slightly increases sharpness on retina devices
// at the expense of slightly slower render times and needing to
// size the image back down if you want to download/upload and be
// true to the images natural size.
const pixelRatio = window.devicePixelRatio;
// const pixelRatio = 1
canvas.width = Math.floor(crop.width * scaleX * pixelRatio);
canvas.height = Math.floor(crop.height * scaleY * pixelRatio);
ctx.scale(pixelRatio, pixelRatio);
ctx.imageSmoothingQuality = 'high';
const cropX = crop.x * scaleX;
const cropY = crop.y * scaleY;
const rotateRads = rotate * TO_RADIANS;
const centerX = image.naturalWidth / 2;
const centerY = image.naturalHeight / 2;
ctx.save();
// 5) Move the crop origin to the canvas origin (0,0)
ctx.translate(-cropX, -cropY);
// 4) Move the origin to the center of the original position
ctx.translate(centerX, centerY);
// 3) Rotate around the origin
ctx.rotate(rotateRads);
// 2) Scale the image
ctx.scale(scale, scale);
// 1) Move the center of the image to the origin (0,0)
ctx.translate(-centerX, -centerY);
ctx.drawImage(
image,
0,
0,
image.naturalWidth,
image.naturalHeight,
0,
0,
image.naturalWidth,
image.naturalHeight
);
ctx.restore();
}
This is how I see the canvas on the browser:
Now, when I convert the image to a blob, then to a file object, I get an image like this :
What I expect to happen is to get an image like this: (exactly like in the preview)
What am I doing wrong ?
you can follow the StackOverflow link: https://stackoverflow.com/questions/15457619/html-canvas-drawing-grid-below-a-plot

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

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>

select a drawn object in canvas

I have a drawn image in canvas with context.drawImage() and I want to select that image to move it with drag and drop in the same canvas (same as MS Paint selection tool). How can I code that in javascript?
function crop(xStart,yStart,xLast,yLast){
canvas.width = xLast - xStart;
canvas.height = yLast - yStart;
context.clearRect(0, 0, canvas.width,canvas.height); drawFromAux(xStart,yStart,xLast,yLast,0,0);
return canvas.toDataURL();
}
// img is my original image
function select(xStart,yStart,xLast,yLast){
selection.src = crop(xStart,yStart,xLast,yLast);
selection.draggable = "true";
context.clearRect(0, 0, canvas.width,canvas.height);
canvas.width = img.width;
canvas.height = img.height;
context.clearRect(0, 0, canvas.width,canvas.height);
context.drawImage(img, 0, 0);
context.clearRect(xStart, yStart, xLast - xStart,yLast - yStart);
context.drawImage(selection,0,0);
}
Using Canvas.js and Pointer.js that should not be that hard.
Things to do:
create image object containing with x, y and raw image
render it to a canvas
listen for mouse moves and check if mouse button is pressed
simple point collision detection between mouse and image on canvas to be able to "select it" and drag it
Loading Pointer.js and Canvas.js:
<script type="text/javascript" src="https://gustavgenberg.github.io/handy-front-end/Canvas.js"></script>
<script type="text/javascript" src="https://gustavgenberg.github.io/handy-front-end/Pointer.js"></script>
1) Creating an image object is not very hard:
const image = {
image: new Image(),
x: canvas.width / 2 - image.width / 2, // centered in canvas
y: canvas.height / 2 - image.height / 2 // centered in canvas
};
image.image.src = ' <url> ';
2) Render that image to the canvas (using Canvas.js)
const canvas = new Canvas('my-canvas', 500, 500).start();
canvas.on('draw', function ( renderer ) {
renderer.drawImage(image.image, image.x, image.y);
});
3) Listening for mouse moves and moving the image (using Pointer.js)
const pointer = new Pointer( canvas.element );
let moveImage = false;
pointer.on('move', function ( event ) {
if( moveImage ) {
image.x += (event.x - pointer.getMoveHistory(-2).x);
image.y += (event.y - pointer.getMoveHistory(-2).y);
}
});
4) Pointer collision detection (using Pointer.js)
pointer.on('down', function () {
moveImage = pointer.touches({ x: image.x, y: image.y, width: image.image.width, height: image.image.height });
});
pointer.on('up', function () {
moveImage = false;
});
JSFiddle: https://jsfiddle.net/GustavGenberg/3h39b9h1/
Hope this helps you :) !

Resize and re-position image on canvas resize

I have an image to be drawn on a canvas with its coordinate. e.g;
var data = {
x: 100, y: 100, // the coord when the image drawn
src: imguri,
scale: 1.6 // the scale when the image drawn
}
and zoom function like below;
var scale = 1.6, width = canvas.width, height = canvas.height
function zoom(positiveOrNegative) {
scale += positiveOrNegative * .2
canvas.width = width * scale
canvas.height = height * scale
loadImage()
}
function loadImage() {
var img = new Image()
img.src = data.src;
img.onload = function() { context.drawImage(img, data.x, data.y) }
}
https://jsfiddle.net/bbuv53u6/
how do I resize and re-position the image to look like it's been zoomed in/out when the canvas is resized?
Use this methods for get a position values .
If you put just
var posX = 10
This variable is fixed but if you dont use variable use method :
VIEW.W(2) // this is 2% from window width .
In this case you no need for any calculation on resize.
Also your example have no zoom effect, you just resize canvas tag element.
Procedures for zoom :
//context.save();
context.translate( x , y );
context.scale(scale, scale);
//context.restore();
Heres object :
var VIEW = {
W : function(per){
if (typeof per === 'undefined'){
return window.innerWidth;
}else{
return window.innerWidth/100 * per;
}
},
H : function(per){
if (typeof per === 'undefined'){
return window.innerHeight;
}
else{
return window.innerHeight/100 * per;
}
},
ASPECT : function(){
return window.innerWidth / window.innerHeight;
},
};
Bonus link :
Zooming with canvas

Getting relative mouse position on canvas after scaling

Problem: I'm working with an HTML canvas. My canvas has a background image that multiple people can draw over in real-time (via socket.io), but drawing breaks if you've zoomed in.
Cause: To calculate where to start and end a line, I normalize input upon capture to be between 0 and 1 inclusive, like so:
// Pseudocode
line.x = mousePosition.x / canvas.width;
line.y = mousePosition.y / canvas.height;
Because of this, the canvas can be of any size and in any position.
To implement a zoom-on-scroll functionality, I simply translate based on the current mouse position, scale the canvas by a factor of 2, then translate back the negative value of the current mouse position (as recommended here).
Here's where the problem lies
When I zoom, the canvas doesn't seem to have a notion of it's original size.
For instance, let's say I have a 1000px square canvas. Using my normalized x and y above, the upper left corner is 0, 0 and the lower right is 1, 1.
I then zoom into the center through scaling by a factor of 2. I would expect that my new upper left would be 0.5, 0.5 and my lower right would be 0.75, 0.75, but it isn't. Even when I zoom in, the upper left is still 0, 0 and the lower right is 1, 1.
The result is that when I zoom in and draw, the lines appear where they would as if I were not zoomed at all. If I zoomed into the center and "drew" in the upper left, I'd see nothing until I scrolled out to see that the line was actually getting drawn on the original upper left.
What I need to know: When zoomed, is there a way to get a read on what your new origin is relative to the un-zoomed canvas, or what amount of the canvas is hidden? Either of these would let me zoom in and draw and have it track correctly.
If I'm totally off base here and there's a better way to approach this, I'm all ears. If you need additional information, I'll provide what I can.
It's not clear to me what you mean by "zoomed".
Zoomed =
made the canvas a different size?
changed the transform on the canvas
used CSS transform?
used CSS zoom?
I'm going to assume it's transform on the canvas in which case it's something like
function getElementRelativeMousePosition(e) {
return [e.offsetX, e.offsetY];
}
function getCanvasRelativeMousePosition(e) {
const pos = getElementRelativeMousePosition(e);
pos[0] = pos[0] * ctx.canvas.width / ctx.canvas.clientWidth;
pos[1] = pos[1] * ctx.canvas.height / ctx.canvas.clientHeight;
return pos;
}
function getComputedMousePosition(e) {
const pos = getCanvasRelativeMousePosition(e);
const p = new DOMPoint(...pos);
const point = inverseOriginTransform.transformPoint(p);
return [point.x, point.y];
}
Where inverseOriginTransform is the inverse of whatever transform you're using to zoom and scroll the contents of the canvas.
const settings = {
zoom: 1,
xoffset: 0,
yoffset: 0,
};
const canvas = document.querySelector('canvas');
const ctx = canvas.getContext('2d');
const lines = [
[[100, 10], [200, 30]],
[[50, 50], [100, 30]],
];
let newStart;
let newEnd;
let originTransform = new DOMMatrix();
let inverseOriginTransform = new DOMMatrix();
function setZoomAndOffsetTransform() {
originTransform = new DOMMatrix();
originTransform.translateSelf(settings.xoffset, settings.yoffset);
originTransform.scaleSelf(settings.zoom, settings.zoom);
inverseOriginTransform = originTransform.inverse();
}
const ui = document.querySelector('#ui')
addSlider(settings, 'zoom', ui, 0.25, 3, draw);
addSlider(settings, 'xoffset', ui, -100, +100, draw);
addSlider(settings, 'yoffset', ui, -100, +100, draw);
draw();
function updateAndDraw() {
draw();
}
function getElementRelativeMousePosition(e) {
return [e.offsetX, e.offsetY];
}
function getCanvasRelativeMousePosition(e) {
const pos = getElementRelativeMousePosition(e);
pos[0] = pos[0] * ctx.canvas.width / ctx.canvas.clientWidth;
pos[1] = pos[1] * ctx.canvas.height / ctx.canvas.clientHeight;
return pos;
}
function getTransformRelativeMousePosition(e) {
const pos = getCanvasRelativeMousePosition(e);
const p = new DOMPoint(...pos);
const point = inverseOriginTransform.transformPoint(p);
return [point.x, point.y];
}
canvas.addEventListener('mousedown', (e) => {
const pos = getTransformRelativeMousePosition(e);
if (newStart) {
} else {
newStart = pos;
newEnd = pos;
}
});
canvas.addEventListener('mousemove', (e) => {
if (newStart) {
newEnd = getTransformRelativeMousePosition(e);
draw();
}
});
canvas.addEventListener('mouseup', (e) => {
if (newStart) {
lines.push([newStart, newEnd]);
newStart = undefined;
}
});
function draw() {
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
ctx.save();
setZoomAndOffsetTransform();
ctx.setTransform(
originTransform.a,
originTransform.b,
originTransform.c,
originTransform.d,
originTransform.e,
originTransform.f);
ctx.beginPath();
for (const line of lines) {
ctx.moveTo(...line[0]);
ctx.lineTo(...line[1]);
}
if (newStart) {
ctx.moveTo(...newStart);
ctx.lineTo(...newEnd);
}
ctx.stroke();
ctx.restore();
}
function addSlider(obj, prop, parent, min, max, callback) {
const valueRange = max - min;
const sliderRange = 100;
const div = document.createElement('div');
div.class = 'range';
const input = document.createElement('input');
input.type = 'range';
input.min = 0;
input.max = sliderRange;
const label = document.createElement('span');
label.textContent = `${prop}: `;
const valueElem = document.createElement('span');
function setInputValue(v) {
input.value = (v - min) * sliderRange / valueRange;
}
input.addEventListener('input', (e) => {
const v = parseFloat(input.value) * valueRange / sliderRange + min;
valueElem.textContent = v.toFixed(1);
obj[prop] = v;
callback();
});
const v = obj[prop];
valueElem.textContent = v.toFixed(1);
setInputValue(v);
div.appendChild(input);
div.appendChild(label);
div.appendChild(valueElem);
parent.appendChild(div);
}
canvas { border: 1px solid black; }
#app { display: flex; }
<div id="app"><canvas></canvas><div id="ui"></div>
Note: I didn't bother making zoom always zoom from the center. To do so would require adjusting xoffset and yoffset as the zoom changes.
Use HTMLElement.prototype.getBoundingClientRect() to get displayed size and position of canvas in DOM. From the displayed size and origin size, calculates the scale of the canvas.
Example:
canvas.addEventListener("click", function (event) {
var b = canvas.getBoundingClientRect();
var scale = canvas.width / parseFloat(b.width);
var x = (event.clientX - b.left) * scale;
var y = (event.clientY - b.top) * scale;
// Marks mouse position
var ctx = canvas.getContext("2d");
ctx.beginPath();
ctx.arc(x, y, 10, 0, 2 * Math.PI);
ctx.stroke();
});

Categories