Dieser Effekt wurde zufällig in einem aktuellen Projekt verwendet, das ein bisschen wie eine Rubbelkarte ist. Auf einem mobilen Gerät wird ein bestimmtes Bild freigerubbelt einem anderen ein Bild anzeigen. Die Darstellungen sind wie folgt:
Bitte klicken Sie rechts für DEMO: DEMO
Diese Methode ist im Internet weit verbreitet. Ich wollte ursprünglich eine Demo online finden und sie anwenden. Aufgrund der Kundenanforderungen ist dies jedoch nicht erforderlich Zumindest kann es besonders flüssig sein, aber die Demo, die ich online gefunden habe, ist zu langsam, was das Spielen unmöglich macht. Deshalb wollte ich einfach selbst einen schreiben, und dieser Artikel dient nur dazu, den Forschungsprozess aufzuzeichnen.
Das erste, was mir in den Sinn kommt, ist die Verwendung von HTML5-Canvas, um diesen Scraping-Effekt zu erzielen. In der Canvas-API kann die Methode „clearRect“ Pixel löschen, aber die Die ClearRect-Methode löscht den rechteckigen Bereich. Schließlich ist der von den meisten Menschen verwendete Radiergummi rund, sodass die leistungsstarke Funktion des Beschneidungsbereichs eingeführt wird, nämlich die Clip-Methode. Die Verwendung ist sehr einfach:
ctx.save() ctx.beginPath() ctx.arc(x2,y2,a,0,2*Math.PI); ctx.clip() ctx.clearRect(0,0,canvas.width,canvas.height); ctx.restore();
Der obige Code realisiert das Löschen eines kreisförmigen Bereichs, dh er implementiert zuerst einen kreisförmigen Pfad und dann diesen Pfad wird als Beschneidungsbereich verwendet und anschließend werden die Pixel gelöscht. Beachten Sie, dass Sie zuerst die Zeichnung-Umgebung speichern müssen. Nach dem Löschen der Pixel müssen Sie die Zeichenumgebung zurücksetzen. Wenn Sie sie nicht zurücksetzen, sind zukünftige Zeichnungen auf diesen Ausschnitt beschränkt Bereich.
Da nun der Löscheffekt vorhanden ist, ist es an der Zeit, den Löscheffekt der Mausbewegung zu beschreiben. Ich werde ihn unten mit der Maus beschreiben, da die mobile Version ähnlich ist. Ersetzen Sie einfach „mousedown“ durch „touchstart“. Ersetzen Sie einfach „mousemove“ durch „touchmove“, „mouseup“ durch „touchend“ und erhalten Sie Koordinatenpunkte von e.clientX zu e.targetTouches[0].pageX.
Um das Löschen von Mausbewegungen zu implementieren, habe ich nur daran gedacht, den kreisförmigen Bereich der Mausposition im ausgelösten Mousemove-Ereignis zu löschen, nachdem ich es geschrieben hatte Wenn sich die Maus sehr schnell bewegt, ist der gelöschte Bereich inkonsistent und der folgende Effekt wird angezeigt. Dies ist offensichtlich nicht der gewünschte Radiergummieffekt.
Da alle Punkte inkohärent sind, müssen diese Punkte als Nächstes verbunden werden Wenn Sie die Zeichenfunktion verwenden, können Sie zwei Punkte direkt über lineTo verbinden und dann zeichnen. Der Beschneidungsbereich im Löscheffekt erfordert jedoch einen geschlossenen Pfad. Wenn Sie einfach zwei Punkte verbinden, können Sie keinen Beschneidungsbereich bilden. Dann dachte ich darüber nach, mithilfe von Berechnungsmethoden die vier Endpunktkoordinaten der Rechtecke in den beiden Löschbereichen zu berechnen, bei denen es sich um das rote Rechteck im Bild unten handelt:
Die Berechnungsmethode ist ebenfalls sehr einfach, da wir die Koordinaten der beiden Endpunkte der Linie kennen, die die beiden Schnittbereiche verbindet, und wir wissen, wie breit die Linie sein soll, die wir wünschen, und die Koordinaten der vier Die Endpunkte des Rechtecks werden leicht zu finden, daher haben wir den folgenden Code:
var asin = a*Math.sin(Math.atan((y2-y1)/(x2-x1))); var acos = a*Math.cos(Math.atan((y2-y1)/(x2-x1))) var x3 = x1+asin;var y3 = y1-acos; var x4 = x1-asin; var y4 = y1+acos; var x5 = x2+asin;var y5 = y2-acos; var x6 = x2-asin;var y6 = y2+acos;
Die Koordinaten der Endpunkte. Auf diese Weise ist der Beschneidungsbereich ein Kreis plus ein Rechteck, und der Code ist wie folgt organisiert:
hastouch = "ontouchstart" window?:= hastouch?"touchstart":"mousedown"= hastouch?"touchmove":"mousemove"= hastouch?"touchend":"mouseup"= hastouch?e.targetTouches[0].pageX:e.clientX-= hastouch?e.targetTouches[0].pageY:e.clientY-0,2*0,0= hastouch?e.targetTouches[0].pageX:e.clientX-= hastouch?e.targetTouches[0].pageY:e.clientY- asin = a*Math.sin(Math.atan((y2-y1)/(x2-x1))); acos = a*Math.cos(Math.atan((y2-y1)/(x2-x1))) x3 = x1+ y3 = y1- x4 = x1- y4 = y1+ x5 = x2+ y5 = y2- x6 = x2- y6 = y2+0,2*0,00,0==
Auf diese Weise wird der Mauslöscheffekt erzielt, aber es gibt immer noch einen Es ist zu beachten, dass dies der Effekt des Löschens einer bestimmten Anzahl von Pixeln ist. Ich verwende imgData, um diesen Effekt zu erzielen. Der Code lautet wie folgt:
var imgData = ctx.getImageData(0,0,canvas.width,canvas.height); var dd = 0; for(var x=0;x<imgData.width;x+=1){ for(var y=0;y<imgData.height;y+=1){ var i = (y*imgData.width + x)*4; if(imgData.data[i+3] > 0){ dd++ } } }if(dd/(imgData.width*imgData.height)<0.4){ canvas.className = "noOp"; }
Rufen Sie imgData ab, durchlaufen Sie die Pixel in imgData und analysieren Sie dann das Alpha in RGBA im Datenarray von imgData, dh analysieren Sie die Transparenz Wenn das Pixel gelöscht wird, beträgt die Transparenz 0, dh die Anzahl der Pixel mit einer Transparenz ungleich 0 in der aktuellen Leinwand wird mit der Gesamtzahl der Pixel auf der Leinwand verglichen Die Transparenz beträgt weniger als 40 %. Das bedeutet, dass mehr als 60 % des Bereichs auf der aktuellen Leinwand gelöscht werden und das Bild automatisch angezeigt werden kann.
此处注意,我是把检查像素这段代码方法mouseup事件里面的,因为这个计算量相对来说还是不小,如果用户狂点鼠标,就会狂触发mouseup事件,也就是会疯狂的触发那个循环计算像素,计算量大到阻塞进程,导致界面卡住的情况,缓解办法如下:加个timeout,延迟执行像素计算,而在每一次点击的时候再清除timeout,也就是如果用户点击很快,这个计算也就触发不了了,还有一个提升的办法就是抽样检查,我上面的写法是逐个像素检查,逐个像素检查的话像素量太大,肯定会卡的,所以可以采用抽样检查,比如每隔30个像素检查一次,修改后的代码如下:
timeout = setTimeout(function(){ var imgData = ctx.getImageData(0,0,canvas.width,canvas.height); var dd = 0; for(var x=0;x<imgData.width;x+=30){ for(var y=0;y<imgData.height;y+=30){ var i = (y*imgData.width + x)*4; if(imgData.data[i+3] >0){ dd++ } } } if(dd/(imgData.width*imgData.height/900)<0.4){ canvas.className = "noOp"; } },100)
这样就可以较大限度的防止用户狂点击了,如果有其他更好的检查方法欢迎给出意见,谢谢。
到了这一步就都写完了,然后就是测试的时候了,结果并不乐观,在android上还是卡啊卡啊,所以又得另想办法,最终发现了绘图环境中的globalCompositeOperation这个属性,这个属性的默认值是source-over,也就是,当你在已有像素上进行绘图时会叠加,但是还有一个属性是destination-out,官方解释就是:在源图像外显示目标图像。只有源图像外的目标图像部分才会被显示,源图像是透明的。好像不太好理解,但是其实自己测试一下就会发现很简单,也就是在已有像素的基础上进行绘图时,你绘制的区域里的已有像素都会被置为透明,直接看张图更容易理解:
globalCompositeOperation属性效果图解。
有了这个属性后,就意味着不需要用到clip,也就不需要用sin、cos什么的计算剪辑区域,直接用条粗线就行了,这样一来就能够很大限度的降低了计算量,同时减少了绘图环境API的调用,性能提升了,在android上运行应该也会流畅很多,下面是修改后的代码:
hastouch = "ontouchstart" window?:= hastouch?"touchstart":"mousedown"= hastouch?"touchmove":"mousemove"= hastouch?"touchend":"mouseup"= hastouch?e.targetTouches[0].pageX:e.clientX-= hastouch?e.targetTouches[0].pageY:e.clientY-= "round"= "round"= a*2= "destination-out"1,0,2* imgData = ctx.getImageData(0,0 dd = 0( x=0;x<imgData.width;x+=30( y=0;y<imgData.height;y+=30 i = (y*imgData.width + x)*4(imgData.data[i+3] > 0++(dd/(imgData.width*imgData.height/900)<0.4){ canvas.className = "noOp"= hastouch?e.targetTouches[0].pageX:e.clientX-= hastouch?e.targetTouches[0].pageY:e.clientY-==
擦除那部分代码就这么一点,也就相当于画图功能,直接设置line属性后通过lineTo进行绘制线条,只要事前把globalCompositeOperation设成destination-out,你所进行的一切绘制,都变成了擦除效果。鼠标滑动触发的事件里面代码也少了很多,绘图对象的调用次数减少了,计算也减少了,性能提升大大滴。
改好代码后就立即用自己的android机子测试了一下,果然如此,跟上一个相比,流畅了很多,至少达到了客户要求的能玩的地步了。
Das obige ist der detaillierte Inhalt vonAusführliche Erläuterung des Beispielcodes zur Umsetzung des Löscheffekts des Radierers in HTML5 (Bild). Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!