gpt4 book ai didi

javascript - 在鼠标位置缩放/缩放

转载 作者:行者123 更新时间:2023-12-03 07:17:35 32 4
gpt4 key购买 nike

我正在努力找出并确定如何根据这个例子放大我的鼠标位置。 (https://stackblitz.com/edit/js-fxnmkm?file=index.js)

let node,
scale = 1,
posX = 0,
posY = 0,
node = document.querySelector('.frame');

const render = () => {
window.requestAnimationFrame(() => {
let val = `translate3D(${posX}px, ${posY}px, 0px) scale(${scale})`
node.style.transform = val
})
}

window.addEventListener('wheel', (e) => {
e.preventDefault();

// Zooming happens here
if (e.ctrlKey) {
scale -= e.deltaY * 0.01;
} else {
posX -= e.deltaX * 2;
posY -= e.deltaY * 2;
}

render();
});

我想要的效果是基于这个例子( https://codepen.io/techslides/pen/zowLd?editors=0010 )放大时。目前我上面的例子只缩放到“视口(viewport)”的中心,但我希望它是我的光标当前所在的位置。

我已经搜索了高低不通过 Canvas 实现的解决方案。任何帮助,将不胜感激!

警告 我使用 wheel 事件的原因是为了模仿 Figma(设计工具)平移和缩放的交互。

最佳答案

将 Canvas 用于可缩放内容
缩放和平移元素是非常有问题的。可以做到,但问题 list 很长。我永远不会实现这样的接口(interface)。
考虑使用 Canvas ,通过 2D 或 WebGL 来显示此类内容,以节省您自己的许多问题。
答案的第一部分是使用 Canvas 实现的。同界面view用于平移和缩放元素的第二个示例。
一个简单的二维 View 。
由于您只是平移和缩放,因此可以使用一种非常简单的方法。
下面的示例实现了一个名为 view 的对象。这保存了当前的比例和位置(平移)
它为用户交互提供了两个功能。

  • 平移函数view.pan(amount)将按 amount.x 持有的像素的距离平移 View , amount.y
  • 缩放功能view.scaleAt(at, amount)将按 amount 缩放(缩小) View (代表比例变化的数字),在 at.x 持有的位置, at.y以像素为单位。

  • 在示例中, View 使用 view.apply() 应用于 Canvas 渲染上下文。并且每当 View 发生变化时都会呈现一组随机框。
    平移和缩放是通过鼠标事件
    使用 Canvas 2D 上下文的示例
    使用鼠标按钮拖动平移,滚轮缩放

    const ctx = canvas.getContext("2d");
    canvas.width = 500;
    canvas.height = 500;
    const rand = (m = 255, M = m + (m = 0)) => (Math.random() * (M - m) + m) | 0;


    const objects = [];
    for (let i = 0; i < 100; i++) {
    objects.push({x: rand(canvas.width), y: rand(canvas.height),w: rand(40),h: rand(40), col: `rgb(${rand()},${rand()},${rand()})`});
    }

    requestAnimationFrame(drawCanvas);

    const view = (() => {
    const matrix = [1, 0, 0, 1, 0, 0]; // current view transform
    var m = matrix; // alias
    var scale = 1; // current scale
    var ctx; // reference to the 2D context
    const pos = { x: 0, y: 0 }; // current position of origin
    var dirty = true;
    const API = {
    set context(_ctx) { ctx = _ctx; dirty = true },
    apply() {
    if (dirty) { this.update() }
    ctx.setTransform(m[0], m[1], m[2], m[3], m[4], m[5])
    },
    get scale() { return scale },
    get position() { return pos },
    isDirty() { return dirty },
    update() {
    dirty = false;
    m[3] = m[0] = scale;
    m[2] = m[1] = 0;
    m[4] = pos.x;
    m[5] = pos.y;
    },
    pan(amount) {
    if (dirty) { this.update() }
    pos.x += amount.x;
    pos.y += amount.y;
    dirty = true;
    },
    scaleAt(at, amount) { // at in screen coords
    if (dirty) { this.update() }
    scale *= amount;
    pos.x = at.x - (at.x - pos.x) * amount;
    pos.y = at.y - (at.y - pos.y) * amount;
    dirty = true;
    },
    };
    return API;
    })();
    view.context = ctx;
    function drawCanvas() {
    if (view.isDirty()) {
    ctx.setTransform(1, 0, 0, 1, 0, 0);
    ctx.clearRect(0, 0, canvas.width, canvas.height);

    view.apply(); // set the 2D context transform to the view
    for (i = 0; i < objects.length; i++) {
    var obj = objects[i];
    ctx.fillStyle = obj.col;
    ctx.fillRect(obj.x, obj.y, obj.h, obj.h);
    }
    }
    requestAnimationFrame(drawCanvas);
    }


    canvas.addEventListener("mousemove", mouseEvent, {passive: true});
    canvas.addEventListener("mousedown", mouseEvent, {passive: true});
    canvas.addEventListener("mouseup", mouseEvent, {passive: true});
    canvas.addEventListener("mouseout", mouseEvent, {passive: true});
    canvas.addEventListener("wheel", mouseWheelEvent, {passive: false});
    const mouse = {x: 0, y: 0, oldX: 0, oldY: 0, button: false};
    function mouseEvent(event) {
    if (event.type === "mousedown") { mouse.button = true }
    if (event.type === "mouseup" || event.type === "mouseout") { mouse.button = false }
    mouse.oldX = mouse.x;
    mouse.oldY = mouse.y;
    mouse.x = event.offsetX;
    mouse.y = event.offsetY
    if(mouse.button) { // pan
    view.pan({x: mouse.x - mouse.oldX, y: mouse.y - mouse.oldY});
    }
    }
    function mouseWheelEvent(event) {
    var x = event.offsetX;
    var y = event.offsetY;
    if (event.deltaY < 0) { view.scaleAt({x, y}, 1.1) }
    else { view.scaleAt({x, y}, 1 / 1.1) }
    event.preventDefault();
    }
    body {
    background: gainsboro;
    margin: 0;
    }
    canvas {
    background: white;
    box-shadow: 1px 1px 1px rgba(0, 0, 0, .2);
    }
    <canvas id="canvas"></canvas>

    使用 element.style.transform 的示例
    此示例使用元素样式转换属性进行缩放和平移。
  • 备注 我使用 2D 矩阵而不是 3D 矩阵,因为这会引入许多与下面使用的简单缩放和平移不兼容的问题。
  • 备注 在所有情况下,CSS 转换都不会应用于元素的左上角。在下面的示例中,原点位于元素的中心。因此,当缩放时,必须通过减去元素大小的一半来调整点的缩放。元素大小不受变换的影响。
  • 备注 边框、填充和边距也会改变原点的位置。与 view.scaleAt(at, amount) 合作at必须相对于元素的左上角像素
  • 备注 当您缩放和平移元素时,您需要考虑更多问题和注意事项,这些问题和注意事项太多,无法放在一个答案中。这就是为什么这个答案以 Canvas 示例开始的原因,因为它是迄今为止管理可缩放视觉内容的更安全的方法。

  • 使用鼠标按钮拖动平移,滚轮缩放。如果您失去位置(放大或平移页面太远,请重新启动代码段)

    const view = (() => {
    const matrix = [1, 0, 0, 1, 0, 0]; // current view transform
    var m = matrix; // alias
    var scale = 1; // current scale
    const pos = { x: 0, y: 0 }; // current position of origin
    var dirty = true;
    const API = {
    applyTo(el) {
    if (dirty) { this.update() }
    el.style.transform = `matrix(${m[0]},${m[1]},${m[2]},${m[3]},${m[4]},${m[5]})`;
    },
    update() {
    dirty = false;
    m[3] = m[0] = scale;
    m[2] = m[1] = 0;
    m[4] = pos.x;
    m[5] = pos.y;
    },
    pan(amount) {
    if (dirty) { this.update() }
    pos.x += amount.x;
    pos.y += amount.y;
    dirty = true;
    },
    scaleAt(at, amount) { // at in screen coords
    if (dirty) { this.update() }
    scale *= amount;
    pos.x = at.x - (at.x - pos.x) * amount;
    pos.y = at.y - (at.y - pos.y) * amount;
    dirty = true;
    },
    };
    return API;
    })();


    document.addEventListener("mousemove", mouseEvent, {passive: false});
    document.addEventListener("mousedown", mouseEvent, {passive: false});
    document.addEventListener("mouseup", mouseEvent, {passive: false});
    document.addEventListener("mouseout", mouseEvent, {passive: false});
    document.addEventListener("wheel", mouseWheelEvent, {passive: false});
    const mouse = {x: 0, y: 0, oldX: 0, oldY: 0, button: false};
    function mouseEvent(event) {
    if (event.type === "mousedown") { mouse.button = true }
    if (event.type === "mouseup" || event.type === "mouseout") { mouse.button = false }
    mouse.oldX = mouse.x;
    mouse.oldY = mouse.y;
    mouse.x = event.pageX;
    mouse.y = event.pageY;
    if(mouse.button) { // pan
    view.pan({x: mouse.x - mouse.oldX, y: mouse.y - mouse.oldY});
    view.applyTo(zoomMe);
    }
    event.preventDefault();
    }
    function mouseWheelEvent(event) {
    const x = event.pageX - (zoomMe.width / 2);
    const y = event.pageY - (zoomMe.height / 2);
    if (event.deltaY < 0) {
    view.scaleAt({x, y}, 1.1);
    view.applyTo(zoomMe);
    } else {
    view.scaleAt({x, y}, 1 / 1.1);
    view.applyTo(zoomMe);
    }
    event.preventDefault();
    }
    body {
    user-select: none;
    -moz-user-select: none;
    }
    .zoomables {
    pointer-events: none;
    border: 1px solid black;
    }
    #zoomMe {
    position: absolute;
    top: 0px;
    left: 0px;
    }
    <img id="zoomMe" class="zoomables" src="/image/C7qq2.png?s=328&g=1">

    关于javascript - 在鼠标位置缩放/缩放,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60190965/

    32 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com