简体   繁体   English

HTML Canvas:CanvasRenderingContext2D 变换需要(重新)设置 canvas 大小?

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

I've modified https://codepen.io/chengarda/pen/wRxoyB我修改了 https://codepen.io/chengarda/pen/wRxoyB

A simple example of internally handling scrolling and zooming of canvas contents using mouse and touch events.使用鼠标和触摸事件在内部处理 canvas 内容的滚动和缩放的简单示例。 No JQuery, no libraries, pure JS.没有 JQuery,没有库,纯 JS。

to use a fixed canvas size and replaced the references to window.innerWidth (and Height) accordingly.使用固定的 canvas 大小并相应地替换对window.innerWidth (和高度)的引用。 The working code resets the canvas size on each draw - removing this causes the code to stop working (current Chrome and Firefox on Ubuntu), and I don't see why.工作代码在每次绘制时重置 canvas 大小 - 删除它会导致代码停止工作(当前的 Chrome 和 Firefox 在 Ubuntu 上),我不明白为什么。 Does setting the canvas size reset context or what is the (side) effect of doing it?是否设置 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/en-US/docs/Web/API/CanvasRenderingContext2D https://developer.mozilla.org/zh-CN/docs/Web/API/CanvasRenderingContext2D

Indeed, setting either the width or the height of your <canvas> will reset your context, all its attributes, and its buffer.实际上,设置<canvas>widthheight将重置您的上下文、它的所有属性和缓冲区。

There is a new ctx.reset() method that does the same, but I believe it's an overkill: indeed, at least in Chrome both this method and the size change will reallocate a new bitmap buffer, which can leave a huge footprint on the memory consumption.有一个新的ctx.reset()方法可以做同样的事情,但我认为这是一种矫枉过正:事实上,至少在 Chrome 中,这个方法和大小变化都会重新分配一个新的 bitmap 缓冲区,这会在memory消费。

All you need in your case is to reset the context's Current Transform Matrix, and clear the buffer, you can do so in two calls:在你的情况下,你需要做的就是重置上下文的当前变换矩阵,并清除缓冲区,你可以在两个调用中这样做:

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

Add these two lines at the beginning of your drawing function and you're good to go.在绘图 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.

相关问题 使用CanvasRenderingContext2D过滤器保存画布 - Saving canvas with CanvasRenderingContext2D filter Javascript-将ImageData放到Canvas元素上-CanvasRenderingContext2D - Javascript - Putting ImageData Onto a Canvas Element - CanvasRenderingContext2D 无法在“CanvasRenderingContext2D”上执行“drawImage”尝试将画布转换为图像 - Failed to execute 'drawImage' on 'CanvasRenderingContext2D Trying to convert a canvas into image canvas.getContext(&#39;2d&#39;)返回Object而不是CanvasRenderingContext2D - `canvas.getContext('2d')` returns `Object` instead of `CanvasRenderingContext2D` Uncaught SecurityError:无法在&#39;CanvasRenderingContext2D&#39;上执行&#39;getImageData&#39;:画布已被跨源数据污染 - Uncaught SecurityError: Failed to execute 'getImageData' on 'CanvasRenderingContext2D': The canvas has been tainted by cross-origin data Angular 4 嵌入 SoundCloud 错误:无法在“CanvasRenderingContext2D”上执行“createPattern”:画布宽度为 0 - Angular 4 Embedding SoundCloud Error: Failed to execute 'createPattern' on 'CanvasRenderingContext2D': The canvas width is 0 Soundcloud嵌入式javascript错误:无法在&#39;CanvasRenderingContext2D&#39;上执行&#39;createPattern&#39;:画布宽度为0 - Soundcloud embedded javascript error : Failed to execute 'createPattern' on 'CanvasRenderingContext2D': The canvas width is 0 无法在“CanvasRenderingContext2D”上执行“createPattern”:图像参数是宽度或高度为 0 的画布元素 - Failed to execute 'createPattern' on 'CanvasRenderingContext2D': The image argument is a canvas element with a width or height of 0 子类化CanvasRenderingContext2D - Subclassing CanvasRenderingContext2D 控制台日志:Uncaught SecurityError:无法在&#39;CanvasRenderingContext2D&#39;上执行&#39;getImageData&#39;:画布已被跨源数据污染 - Console Log: Uncaught SecurityError: Failed to execute 'getImageData' on 'CanvasRenderingContext2D': The canvas has been tainted by cross-origin data
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM