DEV Community

Cover image for Adding Gif Canvas Features : Grid Snap
egfx
egfx

Posted on • Edited on

Adding Gif Canvas Features : Grid Snap

You can create a smooth animation effect without delay just by moving an element around the canvas but you may want to create a more "blocky" animation for a more rigid look. You can create a blocky animation by snapping to a grid. Since gif.com.ai doesn't offer grid snapping out of the box let's make this feature. It's actually surprisingly easy to create a grid snapping feature by creatively mixing the Framing tool that is included in the available tools you can add to your toolbar, and a little custom JS.

Right-click your draggable element, select LimeText IDE and add the HTML, JS ↓
<img data-draggable src="http://example.com/australia.gif"/>
Enter fullscreen mode Exit fullscreen mode
const gifCanvas = document.querySelector('#gifcanvas');
const draggableElements = document.querySelectorAll('[data-draggable]');
const gifCanvasRect = gifCanvas.getBoundingClientRect();

function calculateCellDimensions() {
    const gridOverlay = document.querySelector('.grid-overlay'); // Re-query to get the latest grid overlay
    const cells = gridOverlay.querySelectorAll('.grid-cell');
    if (cells.length === 0) return { width: 0, height: 0 };

    const lastCell = cells[cells.length - 1];
    const coordinates = lastCell.dataset.coordinates.split(',').map(Number);
    const rows = coordinates[1] + 1;
    const cols = coordinates[0] + 1;

    const gridRect = gridOverlay.getBoundingClientRect();
    return {
        width: gridRect.width / cols,
        height: gridRect.height / rows,
        gridRect: gridRect // Return the gridRect for use in calculations
    };
}

draggableElements.forEach(draggable => {
    let offsetX, offsetY;

    draggable.addEventListener('mousedown', function(e) {
        const rect = draggable.getBoundingClientRect();
        offsetX = e.clientX - rect.left; // Calculate the initial offset inside the draggable element
        offsetY = e.clientY - rect.top;

        document.body.style.userSelect = 'none'; // Prevent text selection
    });

    document.addEventListener('mousemove', function(e) {
        if (!offsetX && !offsetY) return; // Check if dragging has started

        const { width: cellWidth, height: cellHeight, gridRect } = calculateCellDimensions(); // Recalculate each time

        // Calculate new position based on mouse location and initial offset
        let newX = e.clientX - offsetX;
        let newY = e.clientY - offsetY;

        // Adjust for live snapping while dragging
        let relativeX = newX - gridRect.left + (draggable.offsetWidth / 2); // Center of the draggable
        let relativeY = newY - gridRect.top + (draggable.offsetHeight / 2); // Center of the draggable

        let snappedX = Math.round(relativeX / cellWidth) * cellWidth - (draggable.offsetWidth / 2);
        let snappedY = Math.round(relativeY / cellHeight) * cellHeight - (draggable.offsetHeight / 2);

        // Confine within gifCanvas
        snappedX = Math.max(gifCanvasRect.left, Math.min(snappedX + gridRect.left, gifCanvasRect.right - draggable.offsetWidth));
        snappedY = Math.max(gifCanvasRect.top, Math.min(snappedY + gridRect.top, gifCanvasRect.bottom - draggable.offsetHeight));

        draggable.style.left = `${snappedX - gifCanvasRect.left}px`; // Adjust position relative to gifCanvas
        draggable.style.top = `${snappedY - gifCanvasRect.top}px`;
    });

    document.addEventListener('mouseup', function() {
        offsetX = null;
        offsetY = null;
        document.body.style.userSelect = ''; // Re-enable text selection
    });
});
Enter fullscreen mode Exit fullscreen mode

After we update the HTML, JS of the element that we intend to drag around, we click the framing tool to the divide the canvas.

Frame tool for gif.com.ai

The framing tool lets us customize the density of the canvas and will act as a guide for the grid snapping as we move the draggable element around.

Let's run our script and record a gif.

Ready to record a gif

Check out the final effect with the grid portions visible.

Final snapping effect

Top comments (0)