Instagram, Instant zoom

x2/x4 zoom for images/videos on timeline

// ==UserScript==
// @name         Instagram, Instant zoom
// @name:ja      Instagram, ズーム
// @namespace    http://tampermonkey.net/
// @version      2025-03-29.2
// @description  x2/x4 zoom for images/videos on timeline
// @description:ja タイムラインで画像をx2/x4ズームできます
// @author       You
// @match        https://*.instagram.com/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=instagram.com
// @grant        GM.addStyle
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';
    GM.addStyle(`
    ._aagv img, video {
        transition: transform 0.06s linear;
    }
    `);

    var zoomMultipoly = 2;

    document.body.addEventListener("mousemove", e => e.target.classList.contains("_aagw") && zoom(e));
    document.body.addEventListener("mouseout", e => e.target.classList.contains("_aagw") && cancelZoom(e));
    document.body.addEventListener("mousedown", e => e.target.classList.contains("_aagw") && moreZoom(e));
    document.body.addEventListener("mouseup", e => e.target.classList.contains("_aagw") && moreZoomOut(e));

    document.body.addEventListener("mousemove", e => e.target.role == "presentation" && zoomVideo(e));
    document.body.addEventListener("mouseout", e => e.target.role == "presentation" && cancelZoomVideo(e));

    function zoom(e) {
        const cover = e.target;
        var image = cover.previousSibling.firstChild;
        if (image.tagName != "IMG") return;

        const x = - (e.offsetX / cover.offsetWidth - 0.5) * (1 - 1 / zoomMultipoly) * 100;
        const y = - (e.offsetY / cover.offsetHeight - 0.5) * (1 - 1 / zoomMultipoly) * 100;
        image.style.transform = `scale(${zoomMultipoly}) translate(${x}%,${y}%)`;
    }

    function cancelZoom(e) {
        const image = e.target.previousSibling.firstChild;
        if (image.tagName != "IMG") return;

        image.style.transform = "";
        zoomMultipoly = 2;
    }

    function moreZoom(e) {
        zoomMultipoly = 4;
        zoom(e);
    }

    function moreZoomOut(e) {
        zoomMultipoly = 2;
        zoom(e);
    }

    function zoomVideo(e) {
        const cover = e.target;
        var video = e.target.parentNode.parentNode.parentNode.firstChild;
        if (video.tagName != "VIDEO") return;

        const x = - (e.offsetX / cover.offsetWidth - 0.5) * (1 - 1 / zoomMultipoly) * 100;
        const y = - (e.offsetY / cover.offsetHeight - 0.5) * (1 - 1 / zoomMultipoly) * 100;
        video.style.transform = `scale(${zoomMultipoly}) translate(${x}%,${y}%)`;
    }

    function cancelZoomVideo(e) {
        var video = e.target.parentNode.parentNode.parentNode.firstChild;
        if (video.tagName != "VIDEO") return;

        video.style.transform = "";
        zoomMultipoly = 2;
    }

})();