Draw on responsively resized canvas while keeping image size - javascript

I currently have a system that take display an image and allows drawing on it using https://github.com/yiom/sketchpad . This plugin (and some others, possibly all) won't draw where I click when the image is resized using css (with max-width/height), the drawing is done on another part of the image. I think the plugin calculate pointer position on the canvas not relative to it's height/width attribute compared to it's displayed height/width.
I did resolve this issue by creating a buffer canvas that will be displayed. Then I take it's pixel height/width and apply it to a new canvas attributes.
This makes the image being resized, which I don't want.
Any idea about how I can resolve this issue? Possibly by using another (free/cheap) plugin.

When your canvas width and height are different with style properties :
<canvas style="width:800px;height:800px" height="400" width="400" class="sketchpad" id="sketchpad"></canvas>
in the sketchpad.js find this :
Sketchpad.prototype._cursorPosition = function(event) {
return {
x: event.pageX - $(this.canvas).offset().left,
y: event.pageY - $(this.canvas).offset().top,
};
};
change it to :
Sketchpad.prototype._cursorPosition = function(event) {
var wd = $(this.canvas).width()*1 / $(this.canvas).attr('width')*1;
var hd = $(this.canvas).height()*1 / $(this.canvas).attr('height')*1;
return {
x: (event.pageX - $(this.canvas).offset().left) / wd ,
y: (event.pageY - $(this.canvas).offset().top) / hd ,
};
};

Related

Canvas mouse events delayed when using resized via CSS

I am playing around with a project to learn more about node.js & html canvases.
In my project I have a canvas that I want to keep a fixed bitmap size, but fill its containing div while maintaining its aspect ratio.
I have applied a size of 500x500 to my canvas element, and then applied the following style in CSS.
canvas {
display: block;
width:100%;
height:100%;
object-fit: contain;
background-color: lightgrey;
}
Inside the javascript initially fill the canvas white so I get something like the below, so far so good.
I hook into the mouse events and use them to draw lines. I use the below function to correctly scale events to the canvas.
function getMousePos(evt) {
var rect = canvas.getBoundingClientRect(); // abs. size of element
var raw_x = evt.clientX||evt.touches[0].clientX;
var raw_y = evt.clientY||evt.touches[0].clientY;
var min_dimension = Math.min(rect.width,rect.height);
var x_offset = 0.5*(rect.width-min_dimension);
var y_offset = 0.5*(rect.height-min_dimension);
return {
x: ((raw_x - rect.left - x_offset) / min_dimension) * canvas.width,
y: ((raw_y - rect.top - y_offset) / min_dimension) * canvas.height
}
}
This works, however when drawing on the canvas when the mouse moves over a band on the right side of the image it doesn't update until the mouse leaves the band. The band is the same size as the space on the left of the canvas so I think its related but I don't know how to investigate. I have no Issues if I resize the window till there is no space on either side of the canvas bitmap (and performance is considerably faster). The below gif should make things more clear.
Does anyone have a suggestion on what could be causing this, or a better way for me to achieve the same effect.
Note: I am running chrome version 80.0.3987.149
For anyone else that comes across this, I couldn't find a good solution, beyond using JavaScript to resize the element when the window resize event occurs.

HTML Canvas Not Displaying Correctly When Resized to Fit Parent Div with 'offsetWidth'

I'm working on a sketchpad application using html canvas and javascript (trying to stay away from jQuery). The canvas needs to be responsive and I've found several methods to do so, but each one stretches out the canvas and makes the sketchpad unusable. It's hard to explain without seeing the problem. Here's the CodePen. Try drawing inside the canvas and you'll see what I'm talking about. The current method I'm using to resize the canvas incorporates offsetWidth and offsetHeight like so:
var sketchpadContainer = [
document.getElementById('container').offsetWidth,
document.getElementById('container').offsetHeight]
var canvas = document.getElementById('sketchpad');
canvas.style.height = sketchpadContainer[1] + "px";
canvas.style.width = sketchpadContainer[0] + "px";
Is there a way to make the canvas responsive while at the same time keeping the dimensions of the sketch intact?
The CSS width and height properties are NOT the same as the width and height attributes on a Canvas element.
If you absolutely need to use css to set width/height, keep a scale factor of your default canvas size, then multiple the target x and y positions of your mouse position by the inverse of the x/y scale factors (or just divide the target position by them).
Using css to resize your canvas is a bit too hacky imo (and will leave your lines blurry), I highly recommend you instead simlpy change with width/height attributes of your canvas and use CanvasRenderingContext2D.scale() to change the size of your lines (A scale factor will still need to be used to calculate your true mouse pos, however)
Simply change
canvas.style.height = sketchpadContainer[1] + "px";
canvas.style.width = sketchpadContainer[0] + "px";
to
canvas.height = sketchpadContainer[1];
canvas.width = sketchpadContainer[0];
Apply CanvasRenderingContext2D.scale() when you first get your context, and then do as I mentioned above. (ctx.lineTo(x,y); -> ctx.lineTo(x/scaleFactorX,y/scaleFactorY); & lastX=x; -> lastX=x/scaleFactorX;)
I.E See HERE

How to scale my canvas in reason, with the zoom scale?

I am working on a multiple web game using JavaScript. My canvas is currently set to the width and the height of my screen.
html
<canvas id = "canvas"></canvas>
javascript
var c=document.getElementById("canvas");
var ctx = c.getContext("2d");
//Making canvas scale;
c.width = window.innerWidth;
c.height = window.innerHeight;
function resize(){
//Add some code
}
My problem is, I do not want my players to zoom out, well not by default. It will make the game look bad and give the players an edge over everyone else. So I need to add some code to go into the resize method, that regardless of scale, the canvas will not be zoomed out. If the end result is something blurry at 300%+ that is fine.
IMPORTANT: the resize function cannot remove or reset the canvas back to default.
There are various ways to scale a canvas.
First off, there are 2 main parameters for the canvas size:
-Canvas Pixel Count. Set via canvas.width = 1000
-Canvas Display Pixel Size. Set via canvas.style.width = '1000px'
If you want all players to see a 1000x1000 region but displaying it fullscreen:
canvas.width = 1000;
canvas.height = 1000;
canvas.style.width = window.innerWidth + 'px';
canvas.style.height = window.innerHeight + 'px';
There is also another option with canvas.style.transform = 'scale(2,2)'.
This method is the closest thing to the browser zoom done via Ctrl+ or Ctrl-.
The big advantage of transform is that the scaling is applied to all DOM children elements. If your game is using HTML for its interface, then this is the way to go. (By applying the scaling on the div containing the canvas + HTML interface.

How to get color that is behind a transparent element?

I am working on a site where the body has many color. When the content is scrolled down the background of the content changes.
In the below image u can see body text with blue color background. So when the content is scrolled down with the scrollbar then the background image should also scroll along with the content.
So what happens is when scroll bar stops at a color like blue then automatically the menu header(Home) background should change to blue.
First of all we should know what we want to do. Step one is actually get a color of the image, thus acces a property of the image.
However the browser cannot get direct information of the image itself(You can, of course, get information from the element of the image). However one webbrowser drawing method let us control every aspect of a image: Canvas.
So first we have to convert our image to an canvas element.
This is fairly simply done with drawImage(img, posx, posy, canvasWidth, canvasHeight);
We will also stretch the canvas image + the element to the entire screen. The reason we want to define to size also at the canvas is because this way the canvas will calculate every new generated pixel, so we can acces every of the new pixels:
var screenWidth = window.innerWidth,
screenHeight = window.innerHeight,
c = document.createElement('canvas');
ctx = c.getContext("2d"),
img = document.getElementById("image");
c.width = screenWidth;
c.height = screenHeight;
ctx.drawImage(img, 0, 0, screenWidth, screenHeight);
$(c).attr('id', 'c');
$('body').prepend(c);
$(img).remove();
An Canvas element as is dynamically made and will fit to the screen size(Note that this is only at first start of the webbrowser. So when you resize the canvas will not resize with the screen).
We remove the image as it is no use anymore. It's a good practive to transform from image to canvas in my opinion. As you will be certain that the image will be loaded in the browser first and then transformed to a canvas element.
Next we're going to acces a pixel. You can acces pixel data with getImageData(offsetX, offsetY, sizeX, sizeY).data; And RGBA color array will be returned.
Now the offsetX and offsetY will be the offset of the element color picker. Note that this elements offset should be relative to the webbrowsers viewport as the background is fixed You can do this with getBoundingClientRect();
The size is just 1x1 as we want 1 pixel.
This all should happen when the user is scrolling, you can catch the scroll event with .scroll(function); :
$(window).scroll(function () {
var offset = document.getElementById('color').getBoundingClientRect(),
offsetX = offset.left,
offsetY = offset.top;
var color = document.getElementById('c').getContext('2d').getImageData(offsetX, offsetY, 1, 1).data;
$('header').css('background-color',"rgba("+ color[0] + ', ' + color[1] + ', '+ color[2] + ', ' + color[3] + ")");
});
Where we add the rgba array colors just with the first 4 indexes(Because and rgba collor pattern never has more as 4 values) with plain css to the header element.
Loading External image into a canvas
When you want to transform an external image to a canvas element you might get this error:
Cross-origin image load denied by Cross-Origin Resource Sharing policy.
It is for security reasons that you can't fully acces this image.
As explained here: HTML 5 canvas getImageData from an externally-loaded image
There are some external hosts that supports this, for example dropbox.
Local
However when you just store it locally it will just work fine.
jsFiddle
This method was tested in chrome, IE and firefox.
I want to use those external (dropbox) files
I'm not sure why this happens but for some reason with this method there are still some security diffecults. To enable this CORS you have to add a image property .crossOrigin = "Anonymous";. However when you load the image with HTML the the element has been made without this property. So you should assign this property when the image is created:
var canvas = document.getElementById("c"),
ctx = canvas.getContext("2d"),
screenWidth = window.innerWidth,
screenHeight = window.innerHeight;
// Using image WITH crossOrigin=anonymous
// Succeeds in Chrome+Mozilla, Still fails in IE
var externalImage2 = new Image();
externalImage2.onload = function(){
canvas.width= screenWidth;
canvas.height= screenHeight;
ctx.drawImage(externalImage2, 0, 0, screenWidth, screenHeight);
// this will FAIL on a CORS violation
}
externalImage2.crossOrigin = "Anonymous";
externalImage2.src="https://dl.dropboxusercontent.com/s/8g8lgmdx341j1d6/rainbow_gradient_horizontal.jpg";
This is just an picture i uploaded to my dropbox where i just shared the photo. Note:
The way to convert a share link to a direct link is to change the domain from "www.dropbox.com" to "dl.dropboxusercontent.com". See https://www.dropbox.com/developers/blog/53/programmatically-download-content-from-share-links.
source: Cross-origin image load from cross-enabled site is denied
This creates an image and puts it into an excisting canvas element. So note that you should have a canvas element into your HTML.
jsFiddle
This method does only work for chrome and firefox as other browsers still have some security issues.
Additional note is the pixel that gets captured is the top-left pixel of the 'color-pick' element. You can adjust this offset if you want it to catch, for example, the middle pixel of the element.
This was a very interesting question. I hope it helped!
well, you may give background color of the home bar as transparent if you have no band between the home and the content.
.home{
background:transparent;
}

Html5 canvas hittest arbitrary shape

I'm trying to develop program which can render images and text in canvas. I tried to handle click on image in canvas, but it work for rectable images.
My question: Did you know solution or frameworks for handle click on visible part of image (not transparent part) in canvas ?
I'm looking for javascript implementation of ActionScript hitTestObject function, if someone familiar with it.
Here is simple algorithm with rectable hit text: http://jsfiddle.net/V92Gn/298/
var hitted = e.clientX >= position.x &&
e.clientX <= position.x + logoImg.width &&
e.clientY >= position.y &&
e.clientY <= position.y + logoImg.height;
Solution using pure JavaScript + canvas
For cases where the hit target is mixed with the background you can do two things:
Create a separate canvas which is stacked on top of the main canvas, draw the target object on that and do the testing of that canvas.
Create an off-screen canvas which contains the target object isolated and test on that
In this example I will use an off-screen canvas.
What we do is to basically replicate the image by creating an off-screen canvas the size of the image and draw in the image when loaded. This will protect the background and keep it transparent no matter what is on the main canvas:
Modified fiddle here
/// create an off-screen canvas to hold image
var ocanvas = document.createElement('canvas');
var octx = ocanvas.getContext('2d');
logoImg.onload=function(){
/// set off-screen canvas to same size as image
ocanvas.width = this.width;
ocanvas.height = this.height;
octx.drawImage(this, 0, 0);
... rest of code
Then using your existing code but with an adjustment for mouse position we can use the hitted variable you use to first check if we are inside the target object.
$(canvas).on('mousemove', function(e) {
/// correct mouse position so it becomes relative to canvas
var r = canvas.getBoundingClientRect(),
x = e.clientX - r.left,
y = e.clientY - r.top;
var hitted = x >= position.x && x <= position.x + logoImg.width &&
y >= position.y && y <= position.y + logoImg.height;
Now that we know we are inside the rectangle we can extract a pixel from the off-screen canvas by compensating for the object position:
if (hitted === true) {
/// extract a single pixel at the adjusted position
var pixel = octx.getImageData(x - position.x, y - position.y, 1, 1).data;
/// set hitted again based on alpha value is 0 or not
hitted = pixel[3] > 0;
}
...
As you can see in the modified fiddle we only change the class you use to red when we are on an actual pixel in the target no matter what the background of it is (when drawn separately).
Finally a couple of words on CORS: In this case, as you use DropBox, you can request CORS usage of the image simply by activating the crossOrigin property on the image object before you set the source:
logoImg.crossOrigin = ''; /// this is the same as setting anonymous
logoImg.src="http://dl.dropbox.com/...";
As DropBox (and image sharing sites such as ImgUr.com) support CORS usage they will allow the request and we can therefor extract pixels from the image.
If the servers didn't allow it however we wouldn't be able to do this. To be sure CORS is ok you should host the image in the same domain as the page when you release it.
I recommend using EaselJS which has a hitTest method similar to how it works in Actionscript:
myDisplayObject.hitTest(localX, localY);
Here you can find some demos that show the technique:
http://shallaazm.com/createjs/easeljs/tutorials/HitTest/

Categories

Resources