💡 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