Blurry to pixelated custom Fade In - javascript

I'm seeking to create a custom fade in with jQuery or JS. I'd like the fade to be exaggerated and go from very blurry, to pixelated, to then the clear crisp image. On page load, one time.
This is for one image. Yes, tradditionaly done via making an animated gif I suppose. But can I write the effect with jQuery?
On page load. Grabbing the image #div >
Image loads blurry > then pixelates in > and then the clear original HQ image resolves.

To pixelate an image you can use canvas directly in a simple way like this (here assuming image has already been loaded):
/// get a block size (see demo for this approach)
size = blocks.value / 100,
w = canvas.width * size,
h = canvas.height * size;
/// draw the original image at a fraction of the final size
ctx.drawImage(img, 0, 0, w, h);
/// turn off image aliasing (see comment below)
ctx.mozImageSmoothingEnabled = false;
ctx.webkitImageSmoothingEnabled = false;
ctx.imageSmoothingEnabled = false; /// future
/// enlarge the minimized image to full size
ctx.drawImage(canvas, 0, 0, w, h, 0, 0, canvas.width, canvas.height);
Here is an online demo.
You can use the slider at the bottom to see various sizes and the speed which would be needed for your transition (use Chrome to see live update when moving the slider) or simply hit the animate button.
As we turn off interpolation (image smoothing) the new enlarged image will appear blocky. This method is also faster than most other approached mainly due to image smoothing can now be turned off in the major browsers.
See this answer for a more complete instruction on how to turn smoothing off in most browsers:
Images in web browser are slightly blurred. How to disable it?
The last drawImage will use a clipped part of the source (here: canvas itself). See here for more details on how to use this method.
As for blur effect I would recommend you to pre-blur the image as this process has a large overhead and the pause will be noticeable on most common computers.
In case you still want to blur the image "live" you can use this stack-blur:
http://www.quasimondo.com/StackBlurForCanvas/StackBlurDemo.html

You could use Close Pixelate by David DeSandro. The Pixelator is a good example of it in action.

Related

HTML5 Canvas efficiency of panning image by Translate vs. Clipping

There's a bunch of questions on panning a background image in a canvas (i.e. to simulate a 'camera' in a game with the character in the center) - and most answers suggest using the canvas' translate method.
But since you have to re-draw the image in each frame anyway, why not just clip it? Does it matter in terms of efficiency?
In particular, is panning like this a bad idea? (This example shows a simplified pan of the camera moving in one direction)
let drawing = new Image();
drawing.src = "img_src";
drawing.onload = function () {
ctx.drawImage(drawing, 0, 0);
let pos = 0
setInterval(() => {
pos += 1
ctx.clearRect(0, 0, cnvs.width, cnvs.height);
ctx.drawImage(drawing, -pos, 0 ); // "pans" the image to the left using the option `drawImage` parameters `sx` and `sy`
}, 33);
};
Example result: fiddle
Thanks in advance!
The main advantage of using the transform matrix to control your camera is that you don't have to update all the elements in your world, you just move the world instead.
So yes, if you are willing to move only a single element (be it the background like in your case), moving only that one element might be a better choice.
But if you need several layers of elements to all move relatively to the camera, then using the transformation matrix is a better choice.
As for the perfs, I didn't ran any benchmarks on this, but I'd suspect it's exactly the same, though beware when messing with the cropping features of drawImage, at least Safari doesn't handle cropping from outside of a source canvas correctly.

Html canvas drawing slows with too many canvas elements

I am working on a slide show editor with a slide-preview strip. I am using electron and react. Each of my slides consists of a background canvas for images/text and a foreground canvas for drawing. My drawing performance is good until I have ~20 slides in my preview strip, then drawing slows down terribly. I am drawing on mouse move event so the user sees it trailing their cursor.
I think I have narrowed down my problem to the ctx.stroke() line when I am drawing on the preview slide. The preview slide is much smaller but has the same resolution as the main canvas the user is actually drawing on.
Is there any way to make this more performant when having >40 canvases on the screen?
apply(ctx, pointIndex) {
let points = this.path.getPoints().slice(pointIndex);
ctx.lineWidth = this.getLineWidth();
if(points.length === 1){
ctx.fillRect(points[0].x - (this.getLineWidth() / 2), points[0].y - (this.getLineWidth() / 2), this.getLineWidth(), this.getLineWidth());
return;
}
ctx.beginPath();
ctx.moveTo(Math.floor(points[0].x), Math.floor(points[0].y));
points.slice(1).forEach(function(point) {
ctx.lineTo(Math.floor(point.x), Math.floor(point.y));
});
ctx.stroke();
ctx.closePath();
ctx.lineWidth = 1;
pointIndex = this.path.getPoints().length - 1;
return pointIndex;
}
To answer your question: Yes, It takes longer to redraw as you increase the total canvas size. You are dramatically increasing the total canvas size by adding full sized canvases even if those canvases are scaled down to be "slide size". BTW, .stroke is not your problem -- it renders very quickly.
Each of your slide canvases has the same resolution as your main canvas. The DOM must remember the original size so total canvas size increases dramatically with each new slide.
A fix is to make the each slide canvases smaller (== the display size), rather than keeping the same resolution as the main canvas. If the user wants to redraw a slide then dynamically create a full-sized canvas from points for that slide. Less total canvas size == better performance.
Canvas has a scaling command that lets you easily take your full-sized canvas content and scale it to smaller display size:
var c=document.createElement('canvas');
var cctx=c.getContext('2d');
c.width=smallSlideWidth;
c.height=smallSlideHeight;
var scale=Math.min((slideWidth/mainCanvasWidth),(slideHeight/mainCanvasHeight));
cctx.scale(scale,scale);
... now draw your slide using the same coordinates as your main canvas
We don't have more of your code, but if you're redrawing every slide canvas all the time -- don't do that!

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 Limitations

I have been unsuccessfully trying to find documentation that details the limitations regarding the Html5 Canvas Element such as what is the largest image file size it can load etc. The reason I ask is that I have been trying to resize image sizes ranging from 50kb to 2.0mb through ctx.scale(), yet it has been horribly inconsistent in that for the same image sometimes ctx.drawImage() will be successful and other times unsuccessful (unsuccessful being no re-scaled image appears in the canvas).
I have also placed console.log(base64) to monitor the result of var base64 = canvas.toDataURL() and have noticed that when successfully resized the resized base64 will be quite a long string as expected and when unsuccessfully resized a string will still appear yet be relatively short and outputs a blank image.
Does this have something to do with memory limitations and the unsuccessful strings wrapping around themselves? If so, what are the memory limitations imposed on the canvas element?
First:
Hard limitations would depend on the browser, not the canvas API.
Even then, browsers are always trying to improve that performance, so that number would always be changing.
But with WebGL and Canvas being used to make games, texture atlases / sprite atlases are HUGE .jpg/.png files.
Chances are very, very good that your images are smaller, and I've frequently used 4MB/5MB/16MB images in canvas for demonstrations.
A huge image (or dozens of them) might crash the tab, if it's big enough, but until that time, canvas hasn't really complained to me.
Second:
There are security-limitations.
Editing photos in canvas comes down to what browser you're on, and whether the file is on the same domain as your website or not.
Third:
When you say that "large files don't work, but they do sometimes..."
...that leads me to believe that your image-loading method is faulty.
If you do something like this:
var canvas = document.createElement("canvas"),
context = canvas.getContext("2d"),
img = new Image();
img.src = "//mydomain.com/myimg.png";
context.drawImage(img, 0, 0, img.width, img.height);
...or anything else which isn't either event-based or callback-based,
then your problem has nothing to do with canvas and has everything to do with callbacks, and that you're trying to draw the image to the canvas before the image is done loading.
If your browser has already cached a copy of the large image, or if a small image only takes a fraction of a second to download, then there's no problem.
If you try downloading an 18MB image, and draw it to the canvas as soon as you set the url for the image, then you're going to get a blank screen, because it hasn't finished loading.
Instead, you need to wait for the image to finish loading, and then draw it to the canvas when it's ready.
var canvas = document.createElement("canvas"),
context = canvas.getContext("2d"),
image = new Image();
image.onload = function () {
var img = this,
width = img.width,
height = img.height;
canvas.width = width;
canvas.height = height;
context.drawImage(img, 0, 0, width, height);
document.body.appendChild(canvas);
};
image.src = "//mydomain.com/path-to-really-huge-image.png";
Now it could be a 16MP image. Nothing will happen until the file is done loading.
Then, the onload event fires, and does the rest of the setup.
You could make it more abstract, and turn it into a nifty program, but it feels like this is the key piece you might be missing.

Taking advantage of foreground and background canvases in HTML5

Right now I have one large canvas where everything is drawn. The canvas is created directly in the JS file:
var canvas = document.createElement ("canvas");
var ctx = canvas.getContext("2d");
canvas.width = document.width;
canvas.height = document.height;
document.body.appendChild(canvas);
It resizes itself to the browser window.
I have a gradient background being drawn onto the one canvas along with all the other elements. The colors for the background are randomly generated at each game mode change (eg when the main menu is toggled to the game, then the level end screen, etc). Currently I'm drawing these onto the canvas like this:
var grad1 = ctx.createRadialGradient(canvas.width / 2, canvas.height / 2, 0, canvas.width / 2, canvas.height / 2, 500);
grad1.addColorStop(0, bgGradStop1);
grad1.addColorStop(1, bgGradStop2);
ctx.fillStyle = grad1;
ctx.fillRect(0, 0, canvas.width, canvas.height);
I read numerous times that having a separate canvas for the background is better for performance. Is there anything specific that I need to do to make this effective or can I simply create another canvas and draw the background using the same exact code, only modified to use the other canvas, like this:
var grad1 = ctxBg.createRadialGradient(canvasBg.width / 2, canvasBg.height / 2, 0, canvasBg.width / 2, canvasBg.height / 2, 500);
grad1.addColorStop(0, bgGradStop1);
grad1.addColorStop(1, bgGradStop2);
ctxBg.fillStyle = grad1;
ctxBg.fillRect(0, 0, canvasBg.width, canvasBg.height);
Or does getting any performance benefit involve some totally different method of using the second canvas?
Also, if it really is just a matter of doing the exact same thing but on a different canvas, would there be any benefit to also for example moving the HUD text to its own canvas while other entities are moving? Even separating various types of entities onto their own canvases? How far does the benefit of multiple canvases actually stretch?
Or does getting any performance benefit involve some totally different method of using the second canvas?
You've got the right idea of how to do it, though I don't think a second canvas is necessary in your case.
The important thing from a performance perspective is that you don't want to have to keep constructing and filling the gradient. If all you're doing in your draw loop is:
ctx.fillStyle = grad1;
ctx.fillRect(0, 0, canvas.width, canvas.height);
Then you should be pretty swell here. I don't think having a second canvas in the background will help you much after that. There might be a slight performance boost, but who wants to have to keep track of additional DOM elements if you don't have to?
It's a little hard to test the performance of having a second canvas behind your main one, but instead of having two large canvases in the DOM one similar alternative is to draw the gradient an in-memory canvas and always call ctx.drawImage(inMemCan, 0, 0); instead of setting the gradient and drawing it. Drawing images is known to be fast, and using two canvases this way is probably close to the speed you could expect from two on-page canvases (and hopefully it would be faster).
So we could test drawing the gradient from an in-memory canvas to your main canvas versus drawing the plain old gradient. Here's a simple test with a large canvas:
http://jsperf.com/gradient-vs-immemcan
They're pretty equal it seems. If this one thing is the only thing in your background I wouldn't worry about it. There are probably bigger performance fish for you to fry first.
I'd save the benefit of multiple canvases for when a relatively complicated background updates rarely but independently of the foreground. If your background was instead made with 30 gradients and some paths, then using a second canvas (or an in-memory canvas to cache an image) would give you a sizable boost.
It resizes itself to the browser window.
Just a reminder that the full screen API works pretty well in webkit and firefox if you want to look into it in the future.

Categories

Resources