Use HTML5 Canvas for wiping and diffusing effects

Source: Internet
Author: User

  
In the 2013, canvas was used to achieve a wiping effect that simulates the interaction of a user erasing a mist on a blurred glass to see a clear view. Fortunately, 2012 years when learning HTML5 canvas, so in a relatively short period of time to achieve a solution "scenario one", and then continue to explore further updated the program "Scenario II", improve the performance of the interaction, but also improve the user experience.
Another project at the beginning of this year, a similar demand, but not erasing the effect, need to be on a map to dynamically show the effect of haze dispel, this interaction demand has a small difficulty, haze edge is blurred, rather than the usual kind of neat.

It is explained here that the basic principle of erasing with canvas is the opposite of visual effect, from visual and intuitive logic, erase is to erase the surface of the image and reveal the underlying pattern, but in the technical implementation, just the opposite, the need to be erased images such as blurred glass is directly displayed , and the clear pattern shown after erasing is drawn on it, which looks like a blurred glass.

Scenario One: Continue to redraw the idea of erasing
The idea of this scenario is to use the canvas's clip method, which can crop the image in a specific shape at the specified location, so that the mask effect can be achieved because the method needs to specify the position at the time of the call, Therefore, the most direct way to realize the dynamic designation of different positions according to the finger or mouse is the basic idea of canvas animation-continuous redrawing, which is called in a continuous loop, and the coordinates passed to the interface are the actual positions of the fingers.

HTML structure:

<div>        <canvas texsrc= "foo.jpg" imgsrc= ' bar.jpg ' width= "height=" [] "style=" position:absolute;width:100px; height:100px; left:0px; top:0px;background:transparent; ' ></canvas></div>

from the HTML structure you can see the "principle opposite": the image that needs to be erased (foo.jpg) is at the bottom, and the image (Bar.jpg) that is displayed after erasing is on the upper level. Because the canvas's background style is set to be transparent, this also visually deceives the user, it is actually in the upper, but because of the transparency, so in addition to the part of the painting, the other part is invisible, forming its illusion in the lower layer.

The main JS code is as follows:

function Canvasdoodle (canvas) {This.canvas=canvas;        This.ctx=canvas.getcontext ("2d");        This.imgsrc=canvas.getattribute ("Imgsrc");        This.width=canvas.width;        This.height=canvas.height;        This.left=parseint (Canvas.style.left);        This.top=parseint (Canvas.style.top);        this.touchx=0;        this.touchy=0;        This.requireloop=false;    This.init ();            } canvasdoodle.prototype={init:function () {Document.body.setAttribute ("Needrefresh", "true");            var _self=this;            This.img=new Image ();            THIS.IMG.SRC=THIS.IMGSRC;                This.canvas.addEventListener (' MouseDown ', function (e) {e.preventdefault ();                _self.requireloop=true;                _self.touchx= e.clientx-_self.left,_self.touchy= E.clienty-_self.top;            _self.loop ();        },false);            This.canvas.addEventListener (' MouseMove ', function (e) {e.preventdefault (); if (_self. requireloop) {_self.touchx= e.clientx-_self.left,_self.touchy= e.clienty-_self.top;            }},false);                This.canvas.addEventListener (' MouseUp ', function (e) {e.preventdefault ();            _self.requireloop=false;        });                }, Loop:function () {if (This.requireloop) {var _self=this;                Requesetanimframe (function () {_self.loop ()});            This.render ();            }}, Render:function () {var _self=this;            _self.ctx.save ();            _self.ctx.beginpath ();            _self.ctx.arc (_self.touchx,_self.touchy,15,0,math.pi*2,true);            _self.ctx.clip ();            _self.ctx.drawimage (_self.img,0,0,_self.width,_self.height,0,0,_self.width,_self.height);        _self.ctx.restore ();    }    }; New Canvasdoodle (document.getElementById (' Canvasdoodle '));

Actual effect

The code is simple, the core part is the Render method, according to the current mouse or finger position in the context of the canvas to draw a circle, and then crop, so that the next drawimage in the context of the text will be drawn in a circular local graphics instead of the entire picture. This way, when the mouse or the finger moves, it will dynamically draw a lot of small circles, which is like erasing.

Requesetanimframe believe that everyone is not unfamiliar, is a circular call. To save performance, a variable requireloop is set to indicate whether a redraw is required, only set to True when the mouse is pressed or touched by the finger, start redrawing the canvas in each loop (that is, call render), and at the end it is set to false and stop drawing.

This scheme is the most primitive solution, with two major drawbacks:

One is the loop call, although there is a requireloop can determine whether to redraw, when the interaction occurs is always in the loop, performance and bad;

The second is the loop call and the user's interaction speed is not synchronized, the ideal situation is the finger or the mouse every change to redraw once, but the reality is not so, in a very fast sliding, each time the dynamically acquired coordinates are not tightly connected, resulting in the effect of erasing is not continuous, experience will become worse.

Scenario Two: No more loops

  The most obvious point of the optimization of the scheme one is the cycle, which is based on the two major flaws. So the main idea of scenario two is to abandon the clip method. Instead, the Strokestyle property of the canvas context is used to refer to the drawing style of the vector line when drawing vector graphics in the canvas, whose values can be color (color value),gradient (Gradient object),pattern (pattern Object ). The solution is to change the drawimg mode of the schema one to the Strokestyle of the canvas context, and then draw the line directly at the time of drawing, because the background of the vector line is the picture that needs to be displayed, thus achieving the effect of erasing. The HTML structure is the same, JS code is as follows:

functionCanvasdoodle (canvas) { This. canvas=Canvas;  This. Ctx=canvas.getcontext ("2d");  This. Imgsrc=canvas.getattribute ("Imgsrc");  This. width=Canvas.width;  This. height=Canvas.height;  This. left=parseint (Canvas.style.left);  This. top=parseint (canvas.style.top);  This. touchx=0;  This. touchy=0;  This. needdraw=false;  This. Init (); } Canvasdoodle.prototype={init:function(){            var_self= This; varimg=NewImage (); Img.onload=function(){                varPat=_self.ctx.createpattern (IMG, "No-repeat"); _self.ctx.strokestyle=Pat; _self.ctx.linecap= "Round"; _self.ctx.linejoin= "Round"; _self.ctx.linewidth= "25"; } img.src= This. imgsrc;  This. Canvas.addeventlistener (' MouseDown ',function(e) {e.preventdefault (); _self.needdraw=true;                _self.ctx.beginpath (); _self.ctx.moveto (E.clientx-_self.left,e.clienty-_self.top); },false);  This. Canvas.addeventlistener (' MouseMove ',function(e) {e.preventdefault (); if(_self.needdraw) {_self.ctx.lineto (E.clientx-_self.left,e.clienty-_self.top);            _self.ctx.stroke (); }        },false);  This. Canvas.addeventlistener (' MouseUp ',function(e) {e.preventdefault (); _self.needdraw=false;        });    }    }; NewCanvasdoodle (document.getElementById (' Canvasdoodle '));

As you can see, there is no loop call, just set the Strokestyle as the image at the time of initialization, the mouse moves directly lineto and then the stroke can be, that is, simple, and efficient, and even if the fast mouse movement will not appear jagged Edge, This improved scheme is therefore a complete replacement for scenario one. The effect is as follows:

New requirements, new solutions

I believe the fog and haze at the beginning of this year should be including women, because a former CCTV reporter at his own expense did a long-term investigation and then made a speech, set off a stir. This demand was put forward at this time. I want to show the fog scattering effect on the map dynamically.

Let's start with something simple.

Here, let's analyze another slightly simpler requirement, and step into it.

Slightly change, it is necessary to show the haze diffusion effect, this will be relatively easy to achieve, because haze can be understood as uniform gray, even if not uniform, can also be expressed as from the center to the edge of varying degrees of gray gradient, the above mentioned canvas Strokestyle can be set as a gradient, Therefore, it is possible to use gradients to blur edges. (And you can also set some CSS3 animations for the canvas, such as from small to large, or from dark to bright).

The HTML structure is basically the same, the main JS code is as follows:

functionCanvasfade (canvas) { This. canvas=Canvas;  This. Ctx=canvas.getcontext ("2d");  This. width=Canvas.width;  This. height=Canvas.height;} Canvasfade.prototype={draw:function(config) {var_self= This; varcfg=config?config:{x:200,y:200,r:120}; varRATIO=CFG.R/2;varGRD = _self.ctx.createradialgradient (cfg.x, CFG.Y, 0.000, cfg.x, Cfg.y, CFG.R); Grd.addcolorstop (0.000, ' Rgba (255, 0, 0, 0.900) '); Grd.addcolorstop (0.5, ' Rgba (255, 0, 0, 0.600) '); Grd.addcolorstop (1.0, ' Rgba (255, 0, 0, 0.000) '); _self.ctx.fillstyle=GRD; _self.ctx.arc (cfg.x, Cfg.y, CFG.R,0,math.pi*2,true);    _self.ctx.fill (); }};varCanvasfade=NewCanvasfade (Jquery (' #theCanvas ') [0]); Canvasfade.draw ({x:100,Y:200Y,R:20R});

It can be seen that the implementation is very simple, this code is used in the test, the color value is red, it looks like a hot zone map, to achieve haze diffusion, only need to change the color value, the actual effect is as follows:

PS. Here is the final effect of multiple calls to draw.

Nut

 Only visually, these two requirements are very close, so it is easy to mistake the goal to achieve. One of the major features of program development is that [there is greater to it then meets the eyes]. Those seemingly cool interactions can be very easy for developers to implement because the vendors may have implemented them at the bottom. Things that seem so simple may take more effort, which is often a cause of friction between product personnel and developers.

We analyze the demand, the haze itself is covered on the map, is not uniform (of course, it can be simplified to even, here is not the main difficulty), the main problem is that the fog scattered after the show is no haze covered map, rather than the pure color (you can see the end of the GIF picture). Edge Blur effect is difficult to implement, because when set Strokestyle, if set as gradient, it is easy to achieve edge blur, but only with the color value, if the Strokestyle is set to pattern can use the picture, but then can't set the gradient, Edge is neat cut, unable to meet demand, after repeated attempts and help Google, finally found a little clue on StackOverflow, seemingly a foreign brother also hit a similar demand, but he was very smart to bypass the Strokestyle this problem, So the ultimate realization is inspired by him, not by my original.

Look at the code first:

functionCliparc (CTX, x, Y, R, f) {vartemp = document.createelement (' Canvas '), TX= Temp.getcontext (' 2d '); Temp.width=Ctx.canvas.width; Temp.height=Ctx.canvas.height; Tx.translate (-temp.width, 0); Tx.shadowoffsetx=Temp.width; Tx.shadowoffsety= 0; Tx.shadowcolor= ' #000 '; Tx.shadowblur=F; Tx.arc (x, Y, R,0, 2 *Math.PI);    Tx.closepath ();    Tx.fill ();    Ctx.save (); Ctx.globalcompositeoperation= ' destination-in '; Ctx.drawimage (temp,0, 0); Ctx.restore ();}functionCanvasfade (canvas) { This. canvas=Canvas;  This. Ctx=canvas.getcontext ("2d");  This. Imgsrc=canvas.getattribute ("Imgsrc");  This. width=Canvas.width;  This. height=Canvas.height;} Canvasfade.prototype={init:function(config) {var_self= This; varCfg=config?config:{x:100,y:100,r:120,f:40}; varimg=NewImage (); Img.onload=function(){            varPat=_self.ctx.createpattern (IMG, "No-repeat"); _self.ctx.fillstyle=Pat; _self.ctx.fillrect (0, 0, _self.width, _self.height);        Cliparc (_self.ctx, cfg.x, Cfg.y, CFG.R, CFG.F);        }; IMG.SRC= This. imgsrc; }};varC=document.queryselector (' #theCanvas ');varcf=NewCanvasfade (c); Cf.init ();

The secret weapon here is the use of shadow, the shadow, when drawing graphics inside the canvas, you can add shadows to the shapes, and shadows can have blurred edges. Here in the actual drawing, create a transition canvas (the canvas itself does not draw graphics, mainly the role of fuzzy shearing), the canvas moved to pan left a width, so that it moved out of the current canvas visual range, However, the subtlety is that the shadowoffsetx of its context is set to the right, so that the shadow of any of its inner shapes falls exactly in the correct position of the current canvas, where its shadow color is black, but has a certain feathering effect (Tx.shadowblur = f ), another secret weapon is globalcompositeoperation, which is used to set how a source (new) image is drawn to the target (existing) image, and its details can be referred to HTTP.// www.html5canvastutorials.com/advanced/html5-canvas-global-composite-operations-tutorial/, actual effects such as:

Can see the actual effect is very good. And the form of animation can be more diverse, and this form can also have more variants to meet the broader needs.

At this point, the method of using canvas to create erase and spread effects is complete. Welcome to criticize ^_^

Use HTML5 Canvas for wiping and diffusing effects

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.