Get canvas click coordinates with css scaling and contain:fit - javascript

For my pixel art website, I have a canvas that is fixed relative to the viewport, scaled with css, and has object-fit:contain; to keep it the size of the div, no matter what is drawn or resized inside.
I need to get the mouse click coordinates without scaling. The scaled coordinates collected by e.y/e.x wouldn't be useful since object-fit:contain; would change aspect ratios.
Here is some dummy code to better explain my question:
var c = document.getElementById("scaledCanvas");
var ctx = c.getContext('2d');
c.height = 16; // varaible height
c.width = 16; // varaible width
for (var x = 0; x < c.width; x++) {
for (var y = 0; y < c.height; y++) {
ctx.fillStyle = "#" + Math.floor(Math.random() * 16777215).toString(16); // pick random color
ctx.fillRect(x, y, 1, 1); // draw pixel on canvas
}
}
c.addEventListener('click', function(e) {
//this is what i need help with, clickX and clickY need to give the pixel coordinates of the canvas, not the screen
let clickX = e.x - this.offsetLeft;
let clickY = e.y - this.offsetTop;
console.log(clickX + ", " + clickY);
}, false);
#scaledCanvas {
width: 50vw;
height: 50vh;
image-rendering: pixelated;
object-fit: contain;
background-color: black;
}
<!DOCTYPE html>
<html>
<body>
<canvas id="scaledCanvas" width="16" height="16">Canvas Not Supported</canvas>
</body>
</html>

Should it be something like that? I take the "pixel's" cell coords.
var c = document.getElementById("scaledCanvas");
var ctx = c.getContext('2d');
c.height = 16; // varaible height
c.width = 16; // varaible width
for (var x = 0; x < c.width; x++) {
for (var y = 0; y < c.height; y++) {
ctx.fillStyle = "#" + Math.floor(Math.random() * 16777215).toString(16); // pick random color
ctx.fillRect(x, y, 1, 1); // draw pixel on canvas
}
}
c.addEventListener('click', function(e) {
let realWidth = c.getBoundingClientRect().width;
let realHeight = c.getBoundingClientRect().height;
let cellSize = (realWidth < realHeight) ? realWidth/16 : realHeight/16;
let clickX = Math.floor((e.x - this.offsetLeft - (realWidth - cellSize*16)/2)/cellSize);
let clickY = Math.floor((e.y - this.offsetTop - (realHeight - cellSize*16)/2)/cellSize);
console.log(clickX + ", " + clickY);
}, false);
#scaledCanvas {
width: 50vw;
height: 50vh;
image-rendering: pixelated;
object-fit: contain;
background-color: black;
}
<canvas id="scaledCanvas" width="16" height="16">Canvas Not Supported</canvas>

Related

Change canvas size on an html page using javascript

The following question is based on an adaption I wish to make of this codepen:
Here is a Pen
I have the following html for displaying the canvas (I want it to be only in a section on the html page and not take up the whole page)
<div class="container">
<canvas id="c"></canvas>
</div>
The javascript for the canvas which shows an animation is below.
<script>
var canvas = document.getElementById('c');
var ctx = canvas.getContext('2d');
var H = window.innerHeight;
var W = window.innerWidth;
canvas.height = H;
canvas.width = W;
var NBR_PARTICLES = 100;
var INTENSITY = 55;
var BLUE_RATIO = 5;
particles = [];
for (var i = 0; i < NBR_PARTICLES; i++) {
particles.push( new particle(i) );
};
function particle(i){
this.size = rand(0, 1.4);
this.x = W / 2;
this.y = H / 2;
this.vx = rand(-1, 1);
this.vy = rand(-1, 1);
this.decay = rand(0.9, 1);
this.c = 0;
}
function draw(){
for (var i = 0; i < NBR_PARTICLES; i++) {
p = particles[i];
ctx.fillStyle = color(p.size);
ctx.beginPath();
ctx.arc(p.x, p.y, p.size, Math.PI*2, false);
ctx.fill();
p.size *= p.decay;
p.x += p.vx;
p.y += p.vy;
p.vx += rand(-.2, .2);
p.vy += rand(-.2, .2);
p.c++;
if(p.size < .2){
particles[i] = new particle(i);
}
};
}
function color(i){
value = 255 - Math.round( (i * (255 / NBR_PARTICLES)) * INTENSITY);
return "rgba(" + value + ", 0, " + Math.round((NBR_PARTICLES - i) / BLUE_RATIO) + ", .75)";
}
setInterval(draw, 33);
/*************************
CONSTRUCT FUNCTIONS
**************************/
function rand(min, max){
value = min + Math.random() * ( max - min );
return value;
}
function cd(args){ // FOR DEBUG
console.dir(args);
}
</script>
I want the canvas size to be a rectangular banner across the page rather than the whole page as it is now.
I have tried changing these variables
var H = window.innerHeight;
var W = window.innerWidth;
canvas.height = H;
canvas.width = W;
to
canvas.height = 200;
canvas.width = 800;
but that doesn't render the animation at all but does appear to resize the canvas.
The CSS here appears to override the existing body as the whole animation takes over the page and my existing content is no longer displayed.
body, html{
margin: 0;
padding: 0;
overflow: hidden;
background-color: #ddd;
}
I tried removing the body from the css but that didn't work at all.
I also, as you can see above, added a div container, hoping that would isolate the canvas but that hasn't worked either.
<div class="container">
<canvas id="c"></canvas>
</div>
How do I adapt this code to make the canvas only render on a portion (width and height of the canvas decided by me) on the screen.
Setting directly the height H and width W , will show canvas correctly at center :
below snippet you can see result centred annilation correctly
var canvas = document.getElementById('c');
var ctx = canvas.getContext('2d');
// set here canvas width and height directly
var H = 200;
var W = 200;
canvas.height = H;
canvas.width = W;
var NBR_PARTICLES = 100;
var INTENSITY = 55;
var BLUE_RATIO = 5;
particles = [];
for (var i = 0; i < NBR_PARTICLES; i++) {
particles.push( new particle(i) );
};
function particle(i){
this.size = rand(0, 1.4);
this.x = W / 2;
this.y = H / 2;
this.vx = rand(-1, 1);
this.vy = rand(-1, 1);
this.decay = rand(0.9, 1);
this.c = 0;
}
function draw(){
for (var i = 0; i < NBR_PARTICLES; i++) {
p = particles[i];
ctx.fillStyle = color(p.size);
ctx.beginPath();
ctx.arc(p.x, p.y, p.size, Math.PI*2, false);
ctx.fill();
p.size *= p.decay;
p.x += p.vx;
p.y += p.vy;
p.vx += rand(-.2, .2);
p.vy += rand(-.2, .2);
p.c++;
if(p.size < .2){
particles[i] = new particle(i);
}
};
}
function color(i){
value = 255 - Math.round( (i * (255 / NBR_PARTICLES)) * INTENSITY);
return "rgba(" + value + ", 0, " + Math.round((NBR_PARTICLES - i) / BLUE_RATIO) + ", .75)";
}
setInterval(draw, 33);
/*************************
CONSTRUCT FUNCTIONS
**************************/
function rand(min, max){
value = min + Math.random() * ( max - min );
return value;
}
function cd(args){ // FOR DEBUG
console.dir(args);
}
body, html{
margin: 0;
padding: 0;
overflow: hidden;
background-color: #ddd;
text-align:center
}
canvas {
border : 1px solid gray;
}
<canvas id="c"></canvas>

Html Drawing on canvas gets resized

I'm trying to simply create a canvas that has a grid with cell sizes of 20 by 20 px.
The canvas is nested within a div with scrollbars.
I've got this dot grid function for a canvas that I got from here
$(function () {
function getDocumentWidth() {
return Math.max(document.documentElement.clientWidth, window.innerWidth || 0);
};
function getDocumentHeight() {
return Math.max(document.documentElement.clientHeight, window.innerHeight || 0)
};
var canvas = document.getElementById('can1');
var context = canvas.getContext('2d');
var vw = getDocumentWidth(),
vh = getDocumentHeight();
// resize the canvas to fill browser window dynamically
window.addEventListener('resize', onResize, false);
function onResize() {
vw = getDocumentWidth();
vh = getDocumentHeight();
resizeCanvas();
}
function resizeCanvas() {
canvas.width = vw;
canvas.height = vh;
drawDots();
}
resizeCanvas();
// grid
function drawGrid() {
var cellW = 20,
cellH = 20;
// vertical lines
for (var x = 0; x <= vw; x += cellW) {
context.moveTo(x, 0); // x, y
context.lineTo(x, vh);
}
// horizontal lines
for (var y = 0; y <= vh; y += cellH) {
context.moveTo(0, y); // x, y
context.lineTo(vw, y);
}
context.strokeStyle = "#cccccc";
context.stroke();
}
// drawGrid();
// dots
function drawDots() {
var r = 1,
cw = 20,
ch = 20;
for (var x = 20; x < vw; x += cw) {
for (var y = 20; y < vh; y += ch) {
context.fillStyle = '#000000';
context.fillRect(x - r / 2, y - r / 2, r, r);
}
}
}
drawDots();
})
And here is how the canvas is placed in html:
<div class="forCanvas" >
<canvas id="can1" class="dropZone ui-widget-header" width=581 height=821 >
</canvas>
</div>
and all the css:
.dropZone {
width: 581px;
height: 821px;
padding: 0;
margin: 0;
background: #fff;
}
.forCanvas {
height: 600px;
width: 680px;
overflow: hidden;
overflow-y: scroll;
overflow-x: auto;
padding: 20px;
background: white;
border: 1px solid #d9d9d9;
border-radius: 5px;
}
The problem is that the size of these cells is not 20 by 20 and the drawing gets resized when changing the browsers dimensions.
From what I understand its a problem with view port width and height vs actual width and height? So I tried setting the width and height as such:(seen above also)
<canvas id="can1" class="dropZone ui-widget-header" width=581 height=821 >
I previously used this function with the same problem:
function drawGrid() {
var cellW = 20,
cellH = 20;
// vertical lines
for (var x = 0; x <= vw; x += cellW) {
context.moveTo(x, 0); // x, y
context.lineTo(x, vh);
}
// horizontal lines
for (var y = 0; y <= vh; y += cellH) {
context.moveTo(0, y); // x, y
context.lineTo(vw, y);
}
context.strokeStyle = "#cccccc";
context.stroke();
}
ALSO HERE ARE PICTURES TO SHOW YOU HOW IT LOOKS:
BEFORE RESIZING
AFTER RESIZING
Any help or explanation would be much appreciated. Thank you!
EDIT
Here is a full code to test out the problem:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>test</title>
<style>
.dropZone {
width: 581px;
height: 821px;
padding: 0;
margin: 0;
background: #fff;
}
</style>
</head>
<body>
<canvas id="can1" class="dropZone ui-widget-header" width=581 height=821 >
</canvas>
<script>
function getDocumentWidth() {
return Math.min(document.documentElement.clientWidth, window.innerWidth || 0);
};
function getDocumentHeight() {
return Math.min(document.documentElement.clientHeight, window.innerHeight || 0)
};
var canvas = document.getElementById('can1');
var context = canvas.getContext('2d');
window.addEventListener('resize', resizeCanvas, false);
function resizeCanvas() {
canvas.width = getDocumentWidth();
canvas.height = getDocumentHeight();
drawDots();
}
function drawDots() {
var r = 1;
for (var x = 20; x < canvas.width; x += 20) {
for (var y = 20; y < canvas.height; y += 20) {
context.fillRect(x - r / 2, y - r / 2, r, r);
}
}
}
resizeCanvas();
</script>
</body>
</html>
To solve this I added the canvas inside a div with the dimensions I wanted. (The problem was caused by setting dimensions to the canvas).
As such:
<div id="can2" class="dropZone ui-widget-header">
<canvas id="can1"></canvas>
</div>

Save only a certain part of an HTML canvas

Is it possible to save or export only a certain part of the canvas rather than the whole canvas?
http://i.stack.imgur.com/hmvYh.jpg
At the moment, when I save the file I get the composition plus the transparent background (light blue in example above) of the entire canvas element I have on the site. What I would like to get is only the gray region (which could be made up of several images and text elements).
Yes you can. Here is the JSFiddle.
First, you need to take a clipping of the image in your canvas. This is pretty simple. I made another canvas (a hidden one) and used the context.drawImage
var hidden_ctx = hidden_canvas.getContext('2d');
hidden_ctx.drawImage(
MainCanvas,
startClippingX,
startClippingY,
clippingWidth,
clippingHeight,
pasteX,
pasteY,
pasteWidth,
pasteHeight
);
Now, we need the Data URL from this canvas so we can download the contents. For this, we will use the canvas.toDataURL method.
var data_url = hidden_canv.toDataURL("image/png");
Now, all we need to do is make a download link (an a element with an href attribute of our data_url) and we're done!
Suppose you have a canvas called oldCanvas and you want to save a rectangular area of width w and height h with its upper left corner at x, y.
Start by making a new canvas element of width w and height h:
var newCanvas = document.createElement('canvas');
newCanvas.width = w;
newCanvas.height = h;
Now copy the rectangular area to the new canvas:
var newContext = newCanvas.getContext('2d');
newContext.drawImage(oldCanvas, x, y, w, h, 0, 0, w, h);
Finally, save the new canvas using toDataUrl() or whatever method you were using previously to save a whole canvas.
For example, you can make an image out of the new canvas:
var newImage = document.createElement('img');
newImage.src = newCanvas.toDataURL();
Then append the new image to the web page:
document.body.appendChild(newImage);
Or maybe you have a container div that you want to append it to.
In any case, once the image is in the document, you can right-click on it and save it as usual.
I've implemented this approach in the following snippet. When you run it, a canvas will be randomly painted. Click and drag on the canvas to select a region that you want to download. A new, downloadable image appears at right.
// Returns a random RGB string (RGBA if alpha is true).
function randomColor(alpha) {
var rgb = [
Math.floor(Math.random() * 255),
Math.floor(Math.random() * 255),
Math.floor(Math.random() * 255)
];
if (alpha) {
rgb.push(Math.random());
}
return 'rgb' + (alpha ? 'a' : '') + '(' + rgb.join(', ') + ')';
}
// Makes a random picture for use in the demonstration.
function makeCanvas() {
var canvas = document.getElementById('oldCanvas'),
context = canvas.getContext('2d'),
width = canvas.width = 400,
height = canvas.height = 500;
context.fillStyle = randomColor();
context.fillRect(0, 0, width, height);
for (var i = 0; i < 200; ++i) {
var x = Math.floor(Math.random() * width),
y = Math.floor(Math.random() * height),
w = Math.floor(Math.random() * width/5),
h = Math.floor(Math.random() * height/5);
context.fillStyle = randomColor(true);
if (Math.floor(Math.random() * 2) === 0) {
context.fillRect(x - w / 2, y - h / 2, w, h);
} else {
context.beginPath();
context.arc(x, y, w, 0, 2 * Math.PI);
context.closePath();
context.fill();
}
}
return canvas;
};
window.onload = function () {
var oldCanvas = makeCanvas(),
oldContext = oldCanvas.getContext('2d'),
targetImage = document.getElementById('targetImage'),
downloadContainer = document.getElementById('downloadContainer'),
selectCanvas = document.getElementById('selectCanvas'),
selectContext = selectCanvas.getContext('2d'),
width = selectCanvas.width = oldCanvas.width,
height = selectCanvas.height = oldCanvas.height;
selectContext.fillStyle = '#000';
downloadContainer.style.left = width + 25 + 'px';
var clipCanvas = document.createElement('canvas'),
clipContext = clipCanvas.getContext('2d');
downloadContainer.appendChild(clipCanvas);
selectCanvas.onmousedown = function (event) {
var x0 = Math.max(0, Math.min(event.clientX, width)),
y0 = Math.max(0, Math.min(event.clientY, height));
targetImage.style.display = 'none';
function update(event) {
var x = Math.max(0, Math.min(event.clientX, width)),
y = Math.max(0, Math.min(event.clientY, height)),
dx = x - x0, w = Math.abs(dx),
dy = y - y0, h = Math.abs(dy);
selectContext.clearRect(0, 0, width, height);
selectContext.fillRect(x0, y0, dx, dy);
clipCanvas.width = w;
clipCanvas.height = h;
if (w*h == 0) {
downloadContainer.style.visibility = 'hidden';
} else {
downloadContainer.style.visibility = 'visible';
clipContext.drawImage(oldCanvas,
x0 + Math.min(0, dx), y0 + Math.min(0, dy), w, h,
0, 0, w, h);
downloadContainer.style.visibility = (w*h == 0 ? 'hidden' : 'visible');
downloadContainer.style.top = Math.min(y0, y) + 'px';
}
};
update(event);
selectCanvas.onmousemove = update;
document.onmouseup = function (event) {
selectCanvas.onmousemove = undefined;
document.onmouseup = undefined;
targetImage.src = clipCanvas.toDataURL();
targetImage.style.display = 'block';
};
};
};
body, div, canvas, img {
margin: 0;
padding: 0;
}
#targetImage {
display: none;
position: absolute;
left: 0;
top: 0;
}
canvas {
display: block;
}
#oldCanvas, #selectCanvas, #downloadContainer {
position: fixed;
}
#downloadContainer {
visibility: hidden;
}
#downloadContainer .label {
position: absolute;
width: 500px;
bottom: -20px;
font-family: sans-serif;
font-size: 17px;
color: #444;
}
#selectCanvas {
opacity: 0.5;
cursor: default;
}
<canvas id="oldCanvas"></canvas>
<canvas id="selectCanvas"></canvas>
<div id="downloadContainer">
<div class="label"> right-click above to download this image </div>
<img id="targetImage">
</div>

How to detect shape on a transparent canvas?

I'm looking for a method of detecting a shape in a transparent PNG.
For example, I will create a transparent canvas of 940x680, then place a fully opaque object somewhere in that canvas.
I want to be able to detect the size (w, h), and top + left location of that object.
Here is an example of the original image:
Here is an example of what I would like to achieve (Bounding box overlay, with top + left margin data):
I've found a resource that does some transparency detection, but I'm not sure how I scale something like this to what I'm looking for.
var imgData,
width = 200,
height = 200;
$('#mask').bind('mousemove', function(ev){
if(!imgData){ initCanvas(); }
var imgPos = $(this).offset(),
mousePos = {x : ev.pageX - imgPos.left, y : ev.pageY - imgPos.top},
pixelPos = 4*(mousePos.x + height*mousePos.y),
alpha = imgData.data[pixelPos+3];
$('#opacity').text('Opacity = ' + ((100*alpha/255) << 0) + '%');
});
function initCanvas(){
var canvas = $('<canvas width="'+width+'" height="'+height+'" />')[0],
ctx = canvas.getContext('2d');
ctx.drawImage($('#mask')[0], 0, 0);
imgData = ctx.getImageData(0, 0, width, height);
}
Fiddle
What you need to do:
Get the buffer
Get a 32-bits reference of that buffer (If your other pixels are transparent then you can use a Uint32Array buffer to iterate).
Scan 0 - width to find x1 edge
Scan width - 0 to find x2 edge
Scan 0 - height to find y1 edge
Scan height - 0 to find y2 edge
These scans can be combined but for simplicity I'll show each step separately.
Online demo of this can be found here.
Result:
When image is loaded draw it in (if the image is small then the rest of this example would be waste as you would know the coordinates when drawing it - assuming here the image you draw is large with a small image inside it)
(note: this is a non-optimized version for the sake of simplicity)
ctx.drawImage(this, 0, 0, w, h);
var idata = ctx.getImageData(0, 0, w, h), // get image data for canvas
buffer = idata.data, // get buffer (unnes. step)
buffer32 = new Uint32Array(buffer.buffer), // get a 32-bit representation
x, y, // iterators
x1 = w, y1 = h, x2 = 0, y2 = 0; // min/max values
Then scan each edge. For left edge you scan from 0 to width for each line (non optimized):
// get left edge
for(y = 0; y < h; y++) { // line by line
for(x = 0; x < w; x++) { // 0 to width
if (buffer32[x + y * w] > 0) { // non-transparent pixel?
if (x < x1) x1 = x; // if less than current min update
}
}
}
For the right edge you just reverse x iterator:
// get right edge
for(y = 0; y < h; y++) { // line by line
for(x = w; x >= 0; x--) { // from width to 0
if (buffer32[x + y * w] > 0) {
if (x > x2) x2 = x;
}
}
}
And the same is for top and bottom edges just that the iterators are reversed:
// get top edge
for(x = 0; x < w; x++) {
for(y = 0; y < h; y++) {
if (buffer32[x + y * w] > 0) {
if (y < y1) y1 = y;
}
}
}
// get bottom edge
for(x = 0; x < w; x++) {
for(y = h; y >= 0; y--) {
if (buffer32[x + y * w] > 0) {
if (y > y2) y2 = y;
}
}
}
The resulting region is then:
ctx.strokeRect(x1, y1, x2-x1, y2-y1);
There are various optimizations you could implement but they depend entirely on the scenario such as if you know approximate placement then you don't have to iterate all lines/columns.
You could do a brute force guess of he placement by skipping x number of pixels and when you found a non-transparent pixel you could make a max search area based on that and so forth, but that is out of scope here.
Hope this helps!
I was in need of something similar to this, just recently. Although the question is answered, I wanted to post my code for a future reference.
In my case, I'm drawing a (font) icon on a blank/transparent canvas, and want to get the bounding box. Even if I know the height of the icon (using font-size, i.e., height), I can't know the width. So I have to calculate it manually.
I'm not sure if there's a clever way to calculate this. First thing that popped into my head was doing it the hard way: manually checking every pixel, and that's what I did.
I think the code is pretty self-explanatory, so I won't do any explanation. I tried to keep the code as clean as possible.
/* Layer 3: The App */
let canvas = document.querySelector("#canvas");
let input = document.querySelector("#input");
let output = document.querySelector("#output");
canvas.width = 256;
canvas.height = 256;
let context = canvas.getContext("2d");
context.font = "200px Arial, sans-serif";
let drawnLetter = null;
drawLetter(input.value);
function drawLetter(letter) {
letter = letter ? letter[0] : null;
if (!letter) {
// clear canvas
context.clearRect(0, 0, canvas.width, canvas.height);
output.textContent = null;
return;
}
if (letter == drawnLetter) {
return;
}
drawnLetter = letter;
// clear canvas
context.clearRect(0, 0, canvas.width, canvas.height);
// draw letter
context.fillText(letter, 50, canvas.height - 50);
// find edges
let boundingBox = findEdges(context);
// mark the edges
context.beginPath();
context.rect(boundingBox.left, boundingBox.top, boundingBox.width, boundingBox.height);
context.lineWidth = 2;
context.strokeStyle = "red";
context.stroke();
// output the values
output.textContent = JSON.stringify(boundingBox, null, " ");
}
/* Layer 2: Interacting with canvas */
function findEdges(context) {
let left = findLeftEdge(context);
let right = findRightEdge(context);
let top = findTopEdge(context);
let bottom = findBottomEdge(context);
// right and bottom are relative to top left (0,0)
return {
left,
top,
right,
bottom,
width : right - left,
height : bottom - top,
};
}
function findLeftEdge(context) {
let imageData = context.getImageData(0, 0, context.canvas.width, context.canvas.height);
let emptyPixel = [0, 0, 0, 0].join();
for (let x = 0; x < context.canvas.width; x++) {
for (let y = 0; y < context.canvas.height; y++) {
let pixel = getPixel(imageData, x, y).join();
if (pixel != emptyPixel) {
return x;
}
}
}
}
function findRightEdge(context) {
let imageData = context.getImageData(0, 0, context.canvas.width, context.canvas.height);
let emptyPixel = [0, 0, 0, 0].join();
for (let x = context.canvas.width - 1; x >= 0; x--) {
for (let y = 0; y < context.canvas.height; y++) {
let pixel = getPixel(imageData, x, y).join();
if (pixel != emptyPixel) {
return x;
}
}
}
}
function findTopEdge(context) {
let imageData = context.getImageData(0, 0, context.canvas.width, context.canvas.height);
let emptyPixel = [0, 0, 0, 0].join();
for (let y = 0; y < context.canvas.height; y++) {
for (let x = 0; x < context.canvas.width; x++) {
let pixel = getPixel(imageData, x, y).join();
if (pixel != emptyPixel) {
return y;
}
}
}
}
function findBottomEdge(context) {
let imageData = context.getImageData(0, 0, context.canvas.width, context.canvas.height);
let emptyPixel = [0, 0, 0, 0].join();
for (let y = context.canvas.height - 1; y >= 0; y--) {
for (let x = 0; x < context.canvas.width; x++) {
let pixel = getPixel(imageData, x, y).join();
if (pixel != emptyPixel) {
return y;
}
}
}
}
/* Layer 1: Interacting with ImageData */
/**
* Returns the pixel array at the specified position.
*/
function getPixel(imageData, x, y) {
return getPixelByIndex(imageData, pos2index(imageData, x, y));
}
/**
* Returns the RGBA values at the specified index.
*/
function getPixelByIndex(imageData, index) {
return [
imageData.data[index + 0],
imageData.data[index + 1],
imageData.data[index + 2],
imageData.data[index + 3],
];
}
/**
* Returns the index of a position.
*/
function pos2index(imageData, x, y) {
return 4 * (y * imageData.width + x);
}
body {
background-color: hsl(0, 0%, 95%);
}
canvas {
background: white;
image-rendering: pixelated;
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAABlBMVEXMzMz////TjRV2AAAAEUlEQVQI12P4z8CAFWEX/Q8Afr8P8erzE9cAAAAASUVORK5CYII=);
zoom: 0.8; /* this counters the scale up (125%) of my screen; can be removed */
}
input {
padding: 0.2em;
margin-top: 0.5em;
}
<canvas id="canvas"></canvas>
<br>
<input type="text" id="input" placeholder="type a letter" value="A" onkeyup="drawLetter(this.value)" />
<pre id="output"></pre>

changing the clicked image in the canvas

i am making this simple canvas . it has three images in it (identical images). i want to change only the clicked image. what i am trying to do is save the image order in a array . and only the change the index of the image which was clicked .
so far i am clueless how to do so ! i have done it with hard coded but cnt do this in general as i dnt know where the user clicked . if i could get the position of the mouse when it was clicked or the id of the image which was clicked i could do something but i am new in html5 so i have no idea how to do so , can any one point me in the right direction ? here is my code
<style>
body {
margin: 0px;
padding: 0px;
}
#myCanvas {
border: 2px solid #9C9898;
}
</style>
<script>
if(window.addEventListener) {
window.addEventListener('load', function ()
{
var my = new Array();
var i=0;
var canvas = document.getElementById("myCanvas");
var context = canvas.getContext("2d");
var imageObj = new Image();
var imageObj1 = new Image();
imageObj.onload = function(){
imageObj1.src = "watermelon-duck-outline.png";
for (var x = 0; x <= 1200;x += 400 ) { alert(i,x);
my[i]=imageObj;
context.drawImage(imageObj, x, 0);
++i;
}
context.drawImage(imageObj, 0, 0);
}
imageObj.src = "tomato2.jpg";
change.addEventListener('mousedown', function () {ButtonDown (change)}, false);
function ButtonDown (change)
{ canvas.width = canvas.width;
i--;
for (var x = 800; x >= 0;x -= 400 ) {
if(i!=2){my[i]=imageObj;}else{my[i]=imageObj1;}
context.drawImage(my[i], x, 0);
i--;
}}
init();
}, false); }
</script>
</head>
<body >
<canvas id="myCanvas" width="1250" height="650">
</canvas>
<button id="change" type="button">Change</button>
</body>
Live Demo
This is how I would do it. Basically you need to store the x,y,width,height of each image. You need these values so that when you click within the canvas you can check the bounds of each image against the x and y of the mouse click. After that its easy to reference which image was clicked. Hopefully the following code is enough to get you on the right track.
var canvas = document.getElementById("myCanvas"),
ctx = canvas.getContext("2d");
ctx.fillStyle = "#f00";
ctx.strokeStyle = "#0f0";
var images = [];
images.push({x : 10, y : 10, width:50, height:50}, {x : 70, y : 10, width:50, height:50}, {x : 130, y : 10, width:50, height:50});
//draw some rects, this would be your images
for(var i = 0; i < images.length; i++){
ctx.fillRect(images[i].x, images[i].y, images[i].width, images[i].height);
}
canvas.onclick = function(e){
var x = 0,
y = 0;
if (e.pageX || e.pageY) {
x = e.pageX;
y = e.pageY;
}
else {
x = e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft;
y = e.clientY + document.body.scrollTop + document.documentElement.scrollTop;
}
x -= canvas.offsetLeft;
y -= canvas.offsetTop;
// This is where you go through all the images, and check the x and y from the mouse event against your images.
for(var i = 0; i < images.length; i++){
if(x > images[i].x && x < images[i].x + images[i].width && y > images[i].y && y < images[i].y + images[i].height){
ctx.strokeRect(images[i].x, images[i].y, images[i].width, images[i].height);
alert('Image ' + i + ' selected');
}
}
}

Categories

Resources