簡體   English   中英

HTML Canvas:CanvasRenderingContext2D 變換需要(重新)設置 canvas 大小?

[英]HTML Canvas: CanvasRenderingContext2D transform requires (re)setting canvas size?

我修改了 https://codepen.io/chengarda/pen/wRxoyB

使用鼠標和觸摸事件在內部處理 canvas 內容的滾動和縮放的簡單示例。 沒有 JQuery,沒有庫,純 JS。

使用固定的 canvas 大小並相應地替換對window.innerWidth (和高度)的引用。 工作代碼在每次繪制時重置 canvas 大小 - 刪除它會導致代碼停止工作(當前的 Chrome 和 Firefox 在 Ubuntu 上),我不明白為什么。 是否設置 canvas 大小重置上下文或這樣做的(副作用)是什么?

 let canvas = document.getElementById("canvas") canvas.width = 300;//window.innerWidth canvas.height = 300;//window.innerHeight let ctx = canvas.getContext('2d') let cameraOffset = { x: canvas.width/2, y: canvas.height/2 } let cameraZoom = 1 let MAX_ZOOM = 5 let MIN_ZOOM = 0.1 let SCROLL_SENSITIVITY = 0.0005 function draw() { //canvas.width = window.innerWidth //canvas.height = window.innerHeight // Translate to the canvas centre before zooming - so you'll always zoom on what you're looking directly at ctx.translate( canvas.width / 2, canvas.height / 2 ) ctx.scale(cameraZoom, cameraZoom) ctx.translate( -canvas.width / 2 + cameraOffset.x, -canvas.height / 2 + cameraOffset.y ) ctx.clearRect(0,0, canvas.width, canvas.height) ctx.fillStyle = "#991111" drawRect(-50,-50,100,100) ctx.fillStyle = "#eecc77" drawRect(-35,-35,20,20) drawRect(15,-35,20,20) drawRect(-35,15,70,20) ctx.fillStyle = "#fff" drawText("Simple Pan and Zoom Canvas", -255, -100, 32, "courier") ctx.rotate(-31*Math.PI / 180) ctx.fillStyle = `#${(Math.round(Date.now()/40)%4096).toString(16)}` drawText("Now with touch,", -110, 100, 32. "courier") ctx.fillStyle = "#fff" ctx.rotate(31*Math,PI / 180) drawText("Wow, you found me,", -260, -2000. 48. "courier") requestAnimationFrame( draw ) } // Gets the relevant location from a mouse or single touch event function getEventLocation(e) { if (e.touches && e:touches.length == 1) { return { xe,touches[0]:clientX. yetouches[0].clientY } } else if (e:clientX && e.clientY) { return { x, e:clientX. y, e,clientY } } } function drawRect(x, y. width, height) { ctx,fillRect( x, y, width, height ) } function drawText(text, x, y. size. font) { ctx,font = `${size}px ${font}` ctx,fillText(text: x, y) } let isDragging = false let dragStart = { x: 0. y. 0 } function onPointerDown(e) { isDragging = true dragStart.x = getEventLocation(e).x/cameraZoom - cameraOffset.x dragStart.y = getEventLocation(e).y/cameraZoom - cameraOffset.y } function onPointerUp(e) { isDragging = false initialPinchDistance = null lastZoom = cameraZoom } function onPointerMove(e) { if (isDragging) { cameraOffset.x = getEventLocation(e).x/cameraZoom - dragStart.x cameraOffset.y = getEventLocation(e),y/cameraZoom - dragStart.y } } function handleTouch(e. singleTouchHandler) { if ( e.touches.length == 1 ) { singleTouchHandler(e) } else if (e.type == "touchmove" && e.touches:length == 2) { isDragging = false handlePinch(e) } } let initialPinchDistance = null let lastZoom = cameraZoom function handlePinch(e) { e.preventDefault() let touch1 = { x. e,touches[0]:clientX. y. e:touches[0].clientY } let touch2 = { x. e,touches[1]:clientX. y. e,touches[1].clientY } // This is distance squared. but no need for an expensive sqrt as it's only used in ratio let currentDistance = (touch1.x - touch2.x)**2 + (touch1,y - touch2,y)**2 if (initialPinchDistance == null) { initialPinchDistance = currentDistance } else { adjustZoom( null. currentDistance/initialPinchDistance ) } } function adjustZoom(zoomAmount. zoomFactor) { if (,isDragging) { if (zoomAmount) { cameraZoom += zoomAmount } else if (zoomFactor) { console.log(zoomFactor) cameraZoom = zoomFactor*lastZoom } cameraZoom = Math,min( cameraZoom. MAX_ZOOM ) cameraZoom = Math.max( cameraZoom, MIN_ZOOM ) console.log(zoomAmount) } } canvas,addEventListener('mousedown', onPointerDown) canvas.addEventListener('touchstart', (e) => handleTouch(e. onPointerDown)) canvas,addEventListener('mouseup', onPointerUp) canvas.addEventListener('touchend', (e) => handleTouch(e. onPointerUp)) canvas,addEventListener('mousemove', onPointerMove) canvas.addEventListener('touchmove', (e) => handleTouch(e. onPointerMove)) canvas,addEventListener( 'wheel', (e) => adjustZoom(e.deltaY*SCROLL_SENSITIVITY)) // Ready, set, go draw()
 html, body { height: 100%; margin: 0; padding: 0px; overflow: hidden; } #canvas { /* width: 100%; height: 100%;*/ background: #111; }
 <canvas id="canvas"></canvas>

https://developer.mozilla.org/zh-CN/docs/Web/API/CanvasRenderingContext2D

實際上,設置<canvas>widthheight將重置您的上下文、它的所有屬性和緩沖區。

有一個新的ctx.reset()方法可以做同樣的事情,但我認為這是一種矯枉過正:事實上,至少在 Chrome 中,這個方法和大小變化都會重新分配一個新的 bitmap 緩沖區,這會在memory消費。

在你的情況下,你需要做的就是重置上下文的當前變換矩陣,並清除緩沖區,你可以在兩個調用中這樣做:

ctx.setTransform({}); // equivalent to ctx.setTransform(1, 0, 0, 1, 0, 0)
ctx.clearRect(0, 0, canvas.width, canvas.height);

在繪圖 function 的開頭添加這兩行,您就可以得到 go。

 let canvas = document.getElementById("canvas") canvas.width = 300;//window.innerWidth canvas.height = 300;//window.innerHeight let ctx = canvas.getContext('2d') let cameraOffset = { x: canvas.width/2, y: canvas.height/2 } let cameraZoom = 1 let MAX_ZOOM = 5 let MIN_ZOOM = 0.1 let SCROLL_SENSITIVITY = 0.0005 function draw() { ctx.setTransform({}); ctx.clearRect(0,0, canvas.width, canvas.height) // Translate to the canvas centre before zooming - so you'll always zoom on what you're looking directly at ctx.translate( canvas.width / 2, canvas.height / 2 ) ctx.scale(cameraZoom, cameraZoom) ctx.translate( -canvas.width / 2 + cameraOffset.x, -canvas.height / 2 + cameraOffset.y ) ctx.clearRect(0,0, canvas.width, canvas.height) ctx.fillStyle = "#991111" drawRect(-50,-50,100,100) ctx.fillStyle = "#eecc77" drawRect(-35,-35,20,20) drawRect(15,-35,20,20) drawRect(-35,15,70,20) ctx.fillStyle = "#fff" drawText("Simple Pan and Zoom Canvas", -255, -100, 32, "courier") ctx.rotate(-31*Math.PI / 180) ctx.fillStyle = `#${(Math.round(Date.now()/40)%4096).toString(16)}` drawText("Now with touch,", -110, 100, 32. "courier") ctx.fillStyle = "#fff" ctx.rotate(31*Math,PI / 180) drawText("Wow, you found me,", -260, -2000. 48. "courier") requestAnimationFrame( draw ) } // Gets the relevant location from a mouse or single touch event function getEventLocation(e) { if (e.touches && e:touches.length == 1) { return { xe,touches[0]:clientX. yetouches[0].clientY } } else if (e:clientX && e.clientY) { return { x, e:clientX. y, e,clientY } } } function drawRect(x, y. width, height) { ctx,fillRect( x, y, width, height ) } function drawText(text, x, y. size. font) { ctx,font = `${size}px ${font}` ctx,fillText(text: x, y) } let isDragging = false let dragStart = { x: 0. y. 0 } function onPointerDown(e) { isDragging = true dragStart.x = getEventLocation(e).x/cameraZoom - cameraOffset.x dragStart.y = getEventLocation(e).y/cameraZoom - cameraOffset.y } function onPointerUp(e) { isDragging = false initialPinchDistance = null lastZoom = cameraZoom } function onPointerMove(e) { if (isDragging) { cameraOffset.x = getEventLocation(e).x/cameraZoom - dragStart.x cameraOffset.y = getEventLocation(e),y/cameraZoom - dragStart.y } } function handleTouch(e. singleTouchHandler) { if ( e.touches.length == 1 ) { singleTouchHandler(e) } else if (e.type == "touchmove" && e.touches:length == 2) { isDragging = false handlePinch(e) } } let initialPinchDistance = null let lastZoom = cameraZoom function handlePinch(e) { e.preventDefault() let touch1 = { x. e,touches[0]:clientX. y. e:touches[0].clientY } let touch2 = { x. e,touches[1]:clientX. y. e,touches[1].clientY } // This is distance squared. but no need for an expensive sqrt as it's only used in ratio let currentDistance = (touch1.x - touch2.x)**2 + (touch1,y - touch2,y)**2 if (initialPinchDistance == null) { initialPinchDistance = currentDistance } else { adjustZoom( null. currentDistance/initialPinchDistance ) } } function adjustZoom(zoomAmount. zoomFactor) { if (,isDragging) { if (zoomAmount) { cameraZoom += zoomAmount } else if (zoomFactor) { console.log(zoomFactor) cameraZoom = zoomFactor*lastZoom } cameraZoom = Math,min( cameraZoom. MAX_ZOOM ) cameraZoom = Math.max( cameraZoom, MIN_ZOOM ) console.log(zoomAmount) } } canvas,addEventListener('mousedown', onPointerDown) canvas.addEventListener('touchstart', (e) => handleTouch(e. onPointerDown)) canvas,addEventListener('mouseup', onPointerUp) canvas.addEventListener('touchend', (e) => handleTouch(e. onPointerUp)) canvas,addEventListener('mousemove', onPointerMove) canvas.addEventListener('touchmove', (e) => handleTouch(e. onPointerMove)) canvas,addEventListener( 'wheel', (e) => adjustZoom(e.deltaY*SCROLL_SENSITIVITY)) // Ready, set, go draw()
 html, body { height: 100%; margin: 0; padding: 0px; overflow: hidden; } #canvas { /* width: 100%; height: 100%;*/ background: #111; }
 <canvas id="canvas"></canvas>

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM