I am studying HTML5 and building my slot game from scratch. when spinning, i want to implement a blur in Y to imageData. I try some algrothim already to imageData, it makes everything become very slow, and i try webkitFilter for canvas's style as well, it is only available for radius. Do you have any good suggestions for it?
You can pre-render your motion blurred slot spinning wheel:
Start with an image that has all the slot elements arranged vertically.
Create vertical motion blur using a temporary canvas (see code below).
To "spin", animate the taller wheel canvas through the slot window.
To efficiently create vertical motion blur:
Repeatedly drawing the vertical slot image.
Increase the Y-offset with each draw.
Decrease the opacity with each draw.
This is the y-blur code:
function motionBlur(wheelContext, image, blurAmount) {
wheelContext.clearRect(0,0,wheelContext.canvas.width,wheelContext.canvas.height)
var y=0;
while (++y <= blurAmount) {
wheelContext.globalAlpha=1/y;
wheelContext.drawImage(image, 0, 0, image.width, image.height+y);
}
}
Related
I'm using html5 canvas to manipulate individual pixels. The canvas is zoomable and draggable, and each pixel is a rectangle, basically I do the following:
pixels.forEach(p => {
context.fillStyle = p.color;
context.fillRect(toScreenX(p.x), toScreenY(p.y), 1*scale, 1*scale);
});
where toScreenX and toScreenY determine the position of the rectangle based on the position of the pixel and the actual scale and offset.
When the zoom scale is 1, everything is okay as shown on picture below:
However, when I start to zoom in, thin white lines start to appear between pixels, and the picture gets distorted as shown in the two figure below:
Here's a short video about the effect: https://imgur.com/a/dvEaZIy
I also tried to use context.putImageData instead of fillRect but for individual pixels, it gets terribly slow.
I'm doing multiplayer game in canvas. I have one canvas for ground, second for player and third for map element above player. Player field of view is much smaller than map so I re-render map when player move every 2px of all 32px.
I recognized that i need to render other players in map canvas, because when player move for example right then other players stutter i their places in 2px margin because of player camera move.
When I put other players in ground canvas then it's all working, but the performance is very bad. Should I put other players in another canvas and transform them separately? I'm rendering only visible for player tiles, but maybe I need to render the whole ground at start and every move display only visible for player tiles? There is a video how it's looking at this moment.
I would try drawing a larger section to a buffer canvas and using drawimage to draw it onto the main canvas each turn.
i.e. map (buffer) canvas is 1600 * 1200
screen is 800 * 800
view center x is vx (this function is to stay centered, you already have logic for your version)
(You may also need Math.max(vx, 0) to stay in view
drawimage(buffercanvas, vx - 400, vy - 400, 800, 800, 0, 0, 800, 800)
I found this sped up my game but I was drawing using lineto() etc, if you are using sprites I am not certain it would offer the same advantage
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!
I have a canvas <canvas></canvas> that displays a graphic of an unclean window. I need to be able to 'clean' the window with my cursor, displaying another image behind it. Just as this website uses their brushes: http://mudcu.be/sketchpad/ but rather than adding to the page, I need to remove content to display the image behind it.
Here's an example of the before and after 'rubbing out':
http://www.titaniumwebdesigns.com/images/forums/before.png http://www.titaniumwebdesigns.com/images/forums/after.png
See this complete DEMO
globalCompositeOperation is one of the most nice features in canvas api.
To achieve the desired effect, I use multiple canvas layers and globalCompositeOperation.
In this solution we have 3 layers:
Layer 1 - bottom Layer 2 - middle
Layer 3 - top
Middle and Top layers are static and only the middle layer is dynamically cleared using globalCompositeOperation.
middleCtx.globalCompositeOperation = "xor";
With globalCompositeOperation = "xor", the brush is drawn over the layer and clears the portion of canvas where it was drawn.
The final result is:
UPDATE:
To verify if the window is fully cleaned I create a hidden canvas with the same size of the others layers and drawn a black rectangle on it. When dragging the mouse over the canvas the layer 2 (middle) is cleared with a circle with transparent gradient color and now we also draw over the hidden canvas a circle with white color (might be any color different of black).
So on, we just verify the pixels of the hidden canvas and if there is no black pixels, the window is cleaned.
To get the image data we need to use something like:
imageData = context.getImageData(x, y, width, height)
and then get the pixels from the image data:
pixels = imageData.data;
The requestAnimationFrame routine is used for performance reason, because getImageData might be slow. The major change in the code is put the brush action inside an animation frame when dragging the mouse instead of do that action in each mouse move event. This allows the processor to have more time to do the pixel data verification.
Here is the modified fiddle with pixel data verification and an alert when the window is cleaned:
jsFiddle link
If you have a canvas where you have drawn a blurred image into it once, you should be able to create that effect by creating a "brush" image (an image containing a semi-transparent circle, with soft edges), and then draw that image in the canvas at the mouse coordinate using:
canvasContext.globalCompositeOperation = "destination-out";
https://developer.mozilla.org/samples/canvas-tutorial/6_1_canvas_composite.html
As soon as you have drawn the blurred image to the canvas, you just need to call the line above once and all drawn images after will use the specified composite operation.
I am trying to achieve grass like bend on a straight line created by using Cubic bezier curve.I have created a straight line using Bezier curve and now i want to bend it from top.But the problem I am facing is I am not able to do it dynamically.The moment I use mousemove for creating the bend,series of curves appear giving the canvas a paint like look which is somthing I dont want.I just want a single curved line.My question is how do I clear the previous lines that have been drawn and restore the latest bended curve??
My CODE:here is my code if you want to have a look at it
Create two canvases stacked on top of each other:
The first one containing the static content, the other only the dynamic content.
This way you will only need to be concerned about clearing the area the grass was drawn in (and this is much faster too as there is no need to redraw static all the time).
Place the two canvases inside a div which have position set to relative, then place the canvases using position set to absolute.
Now you can make a function to draw the static content (you will need to redraw it if the browser window gets resized etc.).
Record the area the grass is drawn within and clear only this (for each grass) in the next frame.
If this last is too complicated, just use a simple clear on the "dynamic" canvas:
ctxDyn.clear(0, 0, canvasDyn.width, canvasDyn.height);
This will also preserve transparency of this canvas, or "layer" if you like.
Use requestAnimationFrame to do the actual animation.
var isPlaying = true;
function animate() {
ctxDyn.clear(0, 0, canvasDyn.width, canvasDyn.height);
drawGrass(); //function to draw the actual grass
if (isPlaying) requestAnimationFrame(animate);
}
animate(); // start
The isPlaying is a flag you can toggle with a button on screen or something similar, to be able to stop the animation. See the link for requestAnimationFrame on how to make this cross-browser as this is still a "young" implementation.
dynCtx and dynCanvas can of course be called what you want.
You need to erase the current contents of the canvas before redrawing your updated "grass". This would involve redrawing everything else that is "static" on the canvas. For example:
function redraw() {
// Erase the current contents
ctx.fillStyle = 'white';
ctx.fill(0, 0, canvas.width, canvas.height);
// Draw the stuff that does not change from frame to frame
drawStaticContent();
// Draw the dynamic content
drawGrass();
}
In your mousemove event, update your grass curve information, then 'redraw'.