モザイク壁画

This script's icon is SCP

// ==UserScript==
// @name         モザイク壁画
// @namespace    http://tampermonkey.net/
// @version      0.3
// @description  This script's icon is SCP
// @author       You
// @match        http://*/*
// @match        https://*/*
// @icon         https://www.google.com/s2/favicons?domain=hertzen.com
// @grant        GM.getValue
// @grant        GM.setValue
// @require      https://code.jquery.com/jquery-3.6.0.js
// ==/UserScript==

(async () => {
    'use strict'
    await import('https://html2canvas.hertzen.com/dist/html2canvas.min.js')
    const sideLen = 300,
          sideHalf = sideLen / 2
    const cv = $('<canvas>').appendTo('body').prop({
        width : sideLen,
        height : sideLen
    }).css({
        position : 'fixed',
        zIndex : 114514,
        cursor : 'move',
        borderRadius : '50%'
    }).on('mousedown',()=>{
        flag = true
    })
    let flag
    $(window).on('mouseup',()=>{
        flag = false
    }).on('mousemove',e=>{
        if(flag) jump(e.clientX - sideHalf, e.clientY - sideHalf)
    })
    let cx, cy
    const jump = (x,y) => {
        GM.setValue('save', [x, y].join('#'));
        [cx, cy] = [x, y]
        cv.css({
            left: x,
            top: y
        })
    }
    jump(...await GM.getValue('save', '100#100').then(v=>v.split('#').map(v=>Number(v))))
    const ctx = cv.get(0).getContext('2d')
    ctx.beginPath()
    ctx.arc(sideHalf, sideHalf, sideHalf, 0, 2 * Math.PI)
    ctx.clip()
    let cv2
    const main = () => {
        ctx.beginPath()
        ctx.drawImage(cv2, cx + $(window).scrollLeft() + 8, cy + $(window).scrollTop(), sideLen, sideLen, 0, 0, sideLen, sideLen)
        requestAnimationFrame(main)
    }
    $(async () => {
        cv2 = toMosaic(await html2canvas($('body').get(0)))
        main()
    })
    const toMosaic = cv => {
        const {width, height} = cv,
              ctx = cv.getContext('2d'),
              {data} = ctx.getImageData(0, 0, width, height),
              unit = 10
        for(let y = 0; y < height; y += unit){
            for(let x = 0; x < width; x += unit){
                const n = (x + y * width) * 4,
                      r = data[n],
                      g = data[n + 1],
                      b = data[n + 2]
                ctx.fillStyle = `rgb(${r},${g},${b})`
                ctx.fillRect(x, y, unit, unit)
            }
        }
        return cv
    };
})()