💡 Pan/zoom with mouse
This commit is contained in:
		
							parent
							
								
									e2daa27b9d
								
							
						
					
					
						commit
						c0fd8de4d6
					
				|  | @ -2,6 +2,7 @@ | |||
| 
 | ||||
| import { html } from 'the-dom'; | ||||
| import { applyChaos } from './chaos'; | ||||
| import { barnsley } from './ifs'; | ||||
| 
 | ||||
| const { body } = html(document); | ||||
| 
 | ||||
|  | @ -9,22 +10,18 @@ const content = body.find('#content'); | |||
| const plotting = body.find('#plotting').node; | ||||
| const ctx = plotting.getContext('2d'); | ||||
| 
 | ||||
| const padding = 40; // padding between the canvas edges and the points
 | ||||
| let dragging = false; | ||||
| let center, zoom = 200; | ||||
| let width, height; | ||||
| 
 | ||||
| const linearTransform = (a, b, c, d, e, f) => point => [ | ||||
|     a * point[0] + b * point[1] + e, | ||||
|     c * point[0] + d * point[1] + f | ||||
| ]; | ||||
| 
 | ||||
| /** | ||||
|  * Re-render the scene from scratch | ||||
|  * | ||||
|  * @return {null} | ||||
|  */ | ||||
| const render = () => { | ||||
|     plotting.width = width + 2 * padding; | ||||
|     plotting.height = height + 2 * padding; | ||||
|     plotting.width = width; | ||||
|     plotting.height = height; | ||||
| 
 | ||||
|     // do not plot (very) small sizes
 | ||||
|     if (width < 1) { | ||||
|  | @ -32,16 +29,27 @@ const render = () => { | |||
|     } | ||||
| 
 | ||||
|     // do the chaos game
 | ||||
|     const image = ctx.getImageData(padding, padding, width, height); | ||||
|     const image = ctx.getImageData(0, 0, width, height); | ||||
|     const color = [0, 0, 0]; | ||||
|     let skip = 50; | ||||
| 
 | ||||
|     applyChaos(image, [0, 0], 500000, [ | ||||
|         linearTransform(0, 0, 0, 0.16, 0, 0), | ||||
|         linearTransform(.85, .04, -.04, .85, 0, 1.6), | ||||
|         linearTransform(.20, -.26, .23, .22, 0, 1.6), | ||||
|         linearTransform(-.15, .28, .26, .24, 0, .44) | ||||
|     ], [.01, .85, .07, .07]); | ||||
|     applyChaos(image, [0, 0], 500000, ...barnsley, point => { | ||||
|         const x = Math.floor(point[0] * zoom + center[0]); | ||||
|         const y = height - Math.floor(point[1] * zoom + center[1]); | ||||
| 
 | ||||
|     ctx.putImageData(image, padding, padding); | ||||
|         if (x >= 0 && x < width && y >= 0 && y < height && skip <= 0) { | ||||
|             const i = (y * width + x) * 4; | ||||
| 
 | ||||
|             image.data[i] = color[0]; | ||||
|             image.data[i + 1] = color[1]; | ||||
|             image.data[i + 2] = color[2]; | ||||
|             image.data[i + 3] = 255; | ||||
|         } | ||||
| 
 | ||||
|         skip -= 1; | ||||
|     }); | ||||
| 
 | ||||
|     ctx.putImageData(image, 0, 0); | ||||
| }; | ||||
| 
 | ||||
| /** | ||||
|  | @ -50,11 +58,60 @@ const render = () => { | |||
|  * @return {null} | ||||
|  */ | ||||
| const resize = () => { | ||||
|     width = content.node.clientWidth - 2 * padding; | ||||
|     height = content.node.clientHeight - 2 * padding; | ||||
|     width = content.node.clientWidth; | ||||
|     height = content.node.clientHeight; | ||||
|     center = [Math.floor(width / 2), Math.floor(height / 2)]; | ||||
| 
 | ||||
|     render(); | ||||
| }; | ||||
| 
 | ||||
| /** | ||||
|  * Zoom on the cursor position when using mouse wheel | ||||
|  */ | ||||
| content.on('wheel', event => { | ||||
|     const delta = event.deltaMode === 0 ? event.deltaY / 53 : event.deltaY; | ||||
|     const newZoom = zoom * (1 - delta * .035); | ||||
| 
 | ||||
|     // which (unprojected) point does the mouse point on?
 | ||||
|     const mouse = [ | ||||
|         (event.offsetX - center[0]) / zoom, | ||||
|         (height - event.offsetY - center[1]) / zoom | ||||
|     ]; | ||||
| 
 | ||||
|     // we need to set the center so that `mouse` stays at
 | ||||
|     // the same position on screen, i.e (vectorially):
 | ||||
|     //    mouse * newZoom + newCenter = mouse * zoom + center
 | ||||
|     // => newCenter = mouse * zoom - mouse * newZoom + center
 | ||||
|     center = [ | ||||
|         mouse[0] * zoom - mouse[0] * newZoom + center[0], | ||||
|         mouse[1] * zoom - mouse[1] * newZoom + center[1] | ||||
|     ]; | ||||
| 
 | ||||
|     zoom = newZoom; | ||||
|     render(); | ||||
| 
 | ||||
|     event.preventDefault(); | ||||
| }); | ||||
| 
 | ||||
| /** | ||||
|  * Pan the content with click-drag action | ||||
|  */ | ||||
| content.on('mousedown', event => dragging = [event.offsetX, event.offsetY]); | ||||
| content.on('mouseup', () => dragging = false); | ||||
| content.on('mousemove', event => { | ||||
|     if (dragging !== false) { | ||||
|         const newMouse = [event.offsetX, event.offsetY]; | ||||
|         const movement = [newMouse[0] - dragging[0], newMouse[1] - dragging[1]]; | ||||
| 
 | ||||
|         center[0] += movement[0]; | ||||
|         center[1] -= movement[1]; | ||||
| 
 | ||||
|         render(); | ||||
|         dragging = newMouse; | ||||
| 
 | ||||
|         event.preventDefault(); | ||||
|     } | ||||
| }); | ||||
| 
 | ||||
| window.onresize = resize; | ||||
| resize(); | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue