>웹 프론트엔드 >H5 튜토리얼 >HTML5에서 지우개 지우기 효과를 구현하기 위한 샘플 코드에 대한 자세한 설명(그림)

HTML5에서 지우개 지우기 효과를 구현하기 위한 샘플 코드에 대한 자세한 설명(그림)

黄舟
黄舟원래의
2017-03-20 16:05:302621검색

이 효과는 최근 프로젝트에서 사용되었는데, 모바일 기기에서 특정 사진 이 긁히는 것과 비슷합니다. 다른 사진을 표시합니다. 렌더링은 다음과 같습니다:

DEMO를 보려면 오른쪽을 클릭하세요. DEMO

이 방법은 인터넷에서 꽤 흔한데, 원래는 온라인에서 데모를 찾아 적용해보고 싶었는데, 안드로이드에서는 안 된다는 걸 알게 됐어요. 최소한 재생은 가능하지만 온라인에서 찾은 데모는 너무 느려서 재생이 불가능합니다. 그래서 그냥 제가 직접 써보고 싶었고, 이 글은 단지 연구 과정을 기록하기 위한 것입니다.

가장 먼저 떠오르는 것은 이러한 스크래핑 효과를 얻기 위해 HTML5 캔버스를 사용하는 것입니다. 캔버스 API에서clearRect 메소드는 픽셀을 지울 수 있지만 ClearRect 방식은 결국 직사각형 영역을 지웁니다. 대부분의 사람들이 사용하는 지우개는 둥글기 때문에 클리핑 영역의 강력한 기능인 클립 방식을 도입한 것입니다. 사용법은 매우 간단합니다:

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();

위의 코드는 원형 영역의 삭제를 구현합니다. 즉, 먼저 원형 경로를 구현한 다음 이 경로를 삭제합니다. 클리핑 영역에서는 픽셀을 지우면 됩니다. 한 가지 주의할 점은 먼저 그리기 환경을 저장해야 한다는 것입니다. 픽셀을 삭제한 후 그리기 환경을 재설정하지 않으면 향후 그림이 해당 클리핑으로 제한됩니다. 영역.

이제 지우기 효과가 나왔으니 이제는 마우스를 움직이는 지우기 효과를 작성해보겠습니다. 아래에서는 모바일 버전도 비슷하기 때문에 마우스를 이용해서 설명하겠습니다. 그냥 mousedown을 touchstart로 바꾸세요. mousemove를 touchmove로 바꾸고, mouseup을 touchend로 바꾸고, e.clientX에서 e.targetTouches[0].pageX로 좌표점을 얻으세요.

마우스 움직임 삭제를 구현하기 위해 마우스가 움직일 때 발생하는 mousemove 이벤트에서 마우스 위치의 원형 영역을 삭제하려고 생각한 것뿐입니다. 마우스가 움직일 때 속도가 매우 빠르면 지워진 영역이 일관되지 않고 다음과 같은 효과가 나타납니다. 이는 분명히 우리가 원하는 지우개 효과가 아닙니다.

 

모든 지점이 일관성이 없으므로 다음으로 해야 할 일은 이러한 지점을 연결하는 것입니다. 그리기 기능을 사용하면 lineTo를 통해 두 점을 직접 연결한 후 그릴 수 있습니다. 그러나 삭제 효과의 클리핑 영역은 단순히 두 점을 연결하는 경우에는 클리핑 영역을 형성할 수 없습니다. 그런 다음 계산 방법을 사용하여 두 개의 지우기 영역에 있는 직사각형의 4개 끝점 좌표를 계산하는 방법을 생각했습니다. 이는 아래 그림의 빨간색 직사각형입니다.

 

계산 방법도 매우 간단합니다. 두 클리핑 영역을 연결하는 선의 두 끝점 좌표와 원하는 선의 너비, 네 끝점의 좌표를 알 수 있기 때문입니다. 찾기 쉽기 때문에 다음과 같은 코드가 있습니다:


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;

좌표. 이렇게 하면 클리핑 영역이 원+직사각형이 되고 코드는 다음과 같이 구성됩니다.

 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==

 이렇게 하면 마우스 지우기 효과를 얻을 수 있지만 여전히 한 가지 지점이 있습니다. 부분 삭제 효과의 경우 특정 수의 픽셀을 지우면 모든 이미지 내용이 자동으로 표시됩니다. 이 효과를 얻기 위해 imgData를 사용합니다. 코드는 다음과 같습니다.

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";
}

imgData를 가져와 imgData의 픽셀을 순회한 후 imgData의 데이터 배열에 있는 rgba의 알파를 분석합니다. 즉, 투명도를 분석합니다. 픽셀이 지워지면 투명도는 0이 됩니다. 즉, 현재 캔버스에서 투명도가 0이 아닌 픽셀 수를 캔버스의 전체 픽셀 수와 비교합니다. 0 투명도는 40% 미만이며 현재 캔버스 영역의 60% 이상이 지워진 후 그림이 자동으로 표시될 수 있음을 의미합니다.

  此处注意,我是把检查像素这段代码方法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机子测试了一下,果然如此,跟上一个相比,流畅了很多,至少达到了客户要求的能玩的地步了。

위 내용은 HTML5에서 지우개 지우기 효과를 구현하기 위한 샘플 코드에 대한 자세한 설명(그림)의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.