최근 프로젝트에서는 작은 픽셀 격자를 지울 수 있고, 프레임 선택의 색상을 변경하며, 다양한 그래픽을 지울 수 있는 픽셀 스타일의 드로잉 보드를 구현하고 싶습니다. 이러한 작은 프로젝트는 단순해 보일 수 있지만 많은 내용을 포함하고 있습니다. 것들.
픽셀 격자 그리기먼저 픽셀 그리드 클래스를 정의해 보겠습니다.
픽셀 = 함수(옵션) { this.x = option.x; this.y = option.y; this.size = option.size ||
x와 y는 중심점의 좌표를 나타냅니다. 처음에는 경로를 정의했습니다.
createPath: 함수(ctx) {if (this.shape === 'circle') {this.createCircle(ctx);} else if (this.shape === 'direct') {this.createRect(ctx);} else {this.createCircle(ctx);}},createCircle: 함수(ctx) {var radius = this.size / 2;ctx.arc(this.x,this.y,radius,0,Math.PI*2);},createRect: function (ctx) {var points = this.getPoints() points.forEach(function (point , i) { ctx[i == 0 ? 'moveTo' : 'lineTo'](point.x, point.y) }) ctx.lineTo(points[0].x, 포인트[0].y);},
픽셀 격자는 원과 직사각형을 지원합니다. 경로가 정의된 후 그려집니다.
그리기: 함수(ctx) {ctx.save();ctx.lineWidth=this.lineWidth;ctx.StrokeStyle=this.StrokeStyle;ctx.fillStyle=this.fillStyle;ctx.beginPath();this.createPath(ctx); ctx.Stroke();if(this.isFill){ctx.fill();}ctx.restore();}
그런 다음 루프를 통해 일괄적으로 픽셀 격자를 만듭니다.
for (var i = stepX + .5; i < canvas.width; i+=stepX) {for (var j = stepY + .5; j < canvas.height; j+=stepY) {var pixel = new Pixel({x : i,y: j,shape: '원'})box.push(pixel);pixel.draw(ctx);}}
완벽해 보이지만, 그려지는 모든 픽셀이 다시 컨텍스트로 그려지고, 캔버스의 상태가 매번 변경된다는 큰 단점이 있습니다. 이는 캔버스가 상대적으로 많으면 렌더링 성능이 저하됩니다. , 성능이 매우 걱정스럽고 드로잉 보드에 일부 작업이 있으므로 캔버스 상태를 자주 변경하는 것은 부적절합니다.
따라서 올바른 접근 방식은 다음과 같습니다. 모든 경로를 정의해야 하며 이를 일괄적으로 캔버스에 한 번에 그리는 것이 가장 좋습니다.
//픽셀 위치 정의 for (var i = stepX + .5; i < canvas.width; i+=stepX) {for (var j = stepY + .5; j < canvas.height; j+=stepY) { var pixel = new Pixel({x: i,y: j,shape: 'circle'})box.push(pixel);}}//일괄 그리기 console.time('time');ctx.beginPath();for (var c = 0; c < box.length; c++) {var 원 = 상자[c];ctx.moveTo(원.x + 3, Circle.y);circle.createPath(ctx);}ctx.closePath();ctx.Stroke();console.timeEnd('time');
이 렌더링 효율성은 매우 빠르고, 캔버스의 상태는 가능한 한 적게 변경된다는 것을 알 수 있습니다. 왜냐하면 컨텍스트의 상태가 변경될 때마다 캔버스가 다시 그려지는데, 이 상태가 전역 상태이기 때문입니다.
픽셀 그리드 상호작용프로젝트의 요구 사항은 캔버스에서 마우스를 누르고 이동하여 픽셀을 지울 수 있다는 것입니다. 여기에는 두 가지 지식 포인트가 포함되어 있습니다. 하나는 마우스 이동 경로에서 픽셀 그리드를 얻는 방법이고 두 번째는 성능 문제입니다. 우리의 요구 사항은 80,000점을 그리는 것입니다. 다른 것은 말할 것도 없고, 그리기와 렌더링은 말할 것도 없고 루핑에만 수십 또는 수백 밀리초가 걸립니다. 먼저 첫 번째 질문을 살펴보겠습니다.
마우스 이동 경로 아래의 그리드 가져오기이 문제를 보면 그리드가 포함된 마우스의 위치를 통해 마우스의 위치를 얻은 다음, 움직일 때마다 위치 계산을 다시 업데이트하는 함수를 작성하는 것을 쉽게 생각할 수 있습니다. 달성할 수 있지만 마우스가 위로 이동하면 빠르게 수행할 수 없으며 각 지점의 위치를 계산할 수 있으며 효과가 일관되지 않습니다. 생각을 바꾸면 마우스가 지나가는 경로의 시작점과 끝점을 명확하게 알 수 있습니다. 그러면 문제는 선분과 원본을 교차시키는 알고리즘이 됩니다. 세그먼트는 브러시의 굵기와 선분이 통과하는 경로가 마우스 이동의 경로이고, 이들과 교차하는 원은 변경이 필요한 그리드입니다. 코드로 변환하면 다음과 같습니다.
함수 sqr(x) { 반환 x * x } 함수 dist2(p1, p2) { 반환 sqr(p1.x - p2.x) + sqr(p1.y - p2.y) } 함수 distToSegmentSquared(p, v, w ) { var l2 = dist2(v, w); if (l2 == 0) return dist2(p, v); ((px - vx) * (wx - vx) + (py - vy) * (wy - vy)) / l2; if (t < 0) return dist2(p, v); if (t > 1) return dist2 (p, w); return dist2(p, { x: vx + t * (wx - vx), y: vy + t * (wy - vy) }); @description 선분이 원과 교차하는지 여부를 계산합니다* @param {x: num, y: num} p 원 중심점* @param {x: num, y: num} v 선분의 시작점* @param {x : num, y: num } w 선분의 끝점*/ function distToSegment(p, v, w) { var offset = pathHeight = Math.min(vx, wx) - 오프셋; var maxX = Math.max(vx, wx) + 오프셋; var minY = Math.min(vy, wy) - 오프셋; var maxY = Math.max(vy, wy) + 오프셋 || px > maxX) && (py < minY || py > maxY)) { return Number.MAX_VALUE } return Math.sqrt(distToSegmentSquared(p, v, w));
특정 논리는 자세히 설명되지 않습니다. 독자는 스스로 코드를 읽을 수 있습니다. 그런 다음 교차하는 그리드를 구하여 박스 안의 데이터를 삭제하고 다시 렌더링하면 효과를 볼 수 있습니다.
같은 방법으로 염색 효과를 만든 다음 캔버스 픽셀 드로잉 보드의 작은 데모를 구현할 수 있습니다. 하지만 염색 효과를 내기 위해서는 첫 번째 그리기 방법을 사용해야 하며, 각 객체의 상태는 독립적이기 때문에 각 픽셀이 객체여야 합니다. 그러나 성능에 대해서는 걱정할 필요가 없습니다. 기본적으로 지연이 느껴지지 않습니다. 효과는 대략 다음과 같습니다.
최근에는 좀 게을러서 일단은 이대로 두겠습니다. 나중에 사진 업로드, 사진 픽셀화, 내보내기 기능을 추가하는 시간을 갖도록 하겠습니다.
위 내용은 이 기사의 전체 내용입니다. 모든 분들의 학습에 도움이 되기를 바랍니다. 또한 모든 분들이 VeVb Wulin Network를 지지해 주시길 바랍니다.