你如何放大特定点(没有画布)?

时间:2017-03-11 23:34:33

标签: javascript image zoom absolute

目标很简单,使用鼠标滚轮,放大到特定点(鼠标所在的位置)。这意味着在缩放后,鼠标将与图片大致相同的位置。

Example of mouse mantaining position after zooming (纯粹是说明性的,如果您使用海豚,鸭子或麦当娜,我不在乎)

我不想使用画布,到目前为止,我尝试过这样的事情:

HTML

<img src="whatever">

JS

function zoom(e){
    var deltaScale = deltaScale || -e.deltaY / 1000;
    var newScale = scale + deltaScale;
    var newWidth = img.naturalWidth * newScale;
    var newHeight = img.naturalHeight * newScale;
    var x = e.pageX;
    var y = e.pageY;
    var newX = x * newWidth / img.width;
    var newY = y * newHeight / img.height;
    var deltaX = newX - x;
    var deltaY = newY - y;
    setScale(newScale);
    setPosDelta(-deltaX,-deltaY);
}

function setPosDelta(dX, dY) {
    var imgPos = getPosition();
    setPosition(imgPos.x + dX, imgPos.y + dY);
}

function getPosition() {
    var x = parseFloat(img.style.left);
    var y = parseFloat(img.style.top);
    return {
        x: x,
        y: y
    }
}

function setScale(n) {
    scale = n;
    img.width = img.naturalWidth * n;
    img.height = img.naturalHeight * n;
}

这样做的目的是计算海豚眼睛的 x,y坐标 之前的之后的,计算这两点之间的距离后,从左,顶位置对其进行减法,以纠正变焦位移,但没有特别成功。

变焦发生自然地将图像向右和向下延伸,因此校正尝试向左拉回到顶部,以便将鼠标保持在那该死的海豚眼上!但它肯定没有。

告诉我,代码/数学有什么问题?考虑到除了画布之外我找不到任何解决方案,我觉得这个问题不是太宽泛。

谢谢!

[编辑]重要

CSS转换顺序很重要,如果您按照选定的答案,请确保先按顺序排序,然后再按比例排序。 CSS变换向后执行(从右到左),因此首先处理缩放,然后处理翻译。

3 个答案:

答案 0 :(得分:6)

这是缩放到某一点的实现。该代码使用CSS 2D transform,并包括在单击和拖动时平移图像。这很容易,因为规模没有变化。

缩放时的技巧是首先使用当前比例标准化偏移量(换句话说:除以当前比例),然后将新比例应用于该标准化偏移。这使光标准确地保持在与刻度无关的位置。

&#13;
&#13;
var scale = 1,
    panning = false,
    xoff = 0,
    yoff = 0,
    start = {x: 0, y: 0},
    doc = document.getElementById("document");

function setTransform() {
  doc.style.transform = "translate(" + xoff + "px, " + yoff + "px) scale(" + scale + ")";
}

doc.onmousedown = function(e) {
  e.preventDefault();
  start = {x: e.clientX - xoff, y: e.clientY - yoff};    
  panning = true;
}

doc.onmouseup = function(e) {
  panning = false;
}

doc.onmousemove = function(e) {
  e.preventDefault();         
  if (!panning) {
    return;
  }
  xoff = (e.clientX - start.x);
  yoff = (e.clientY - start.y);
  setTransform();
}

doc.onwheel = function(e) {
    e.preventDefault();
    // take the scale into account with the offset
    var xs = (e.clientX - xoff) / scale,
        ys = (e.clientY - yoff) / scale,
        delta = (e.wheelDelta ? e.wheelDelta : -e.deltaY);

    // get scroll direction & set zoom level
    (delta > 0) ? (scale *= 1.2) : (scale /= 1.2);

    // reverse the offset amount with the new scale
    xoff = e.clientX - xs * scale;
    yoff = e.clientY - ys * scale;

    setTransform();          
}
&#13;
html, body {
  margin: 0;
  padding: 0;
  width: 100%;
  height: 100%;
  overflow: hidden;
}

#document {
  width: 100%;
  height: 100%;
  transform-origin: 0px 0px;
  transform: scale(1) translate(0px, 0px);
}
&#13;
<div id="document">
  <img style="width: 100%"
       src="http://lorempixel.com/output/animals-q-c-1920-1920-9.jpg" />
</div>
    
&#13;
&#13;
&#13;

答案 1 :(得分:3)

这是一个更接近原始想法的实现,使用顶部和左侧偏移并修改图像的宽度属性,而不是在我的其他答案中使用css变换。

&#13;
&#13;
var scale = 1.0,
  img = document.getElementById("image"),
  deltaX = 0,
  deltaY = 0;

// set the initial scale once the image is loaded
img.onload = function() {
  scale = image.offsetWidth / image.naturalWidth;
}

img.onwheel = function(e) {
  e.preventDefault();

  // first, remove the scale so we have the native offset
  var xoff = (e.clientX - deltaX) / scale,
    yoff = (e.clientY - deltaY) / scale,
    delta = (e.wheelDelta ? e.wheelDelta : -e.deltaY);

  // get scroll direction & set zoom level
  (delta > 0) ? (scale *= 1.05) : (scale /= 1.05);

  // limit the smallest size so the image does not disappear
  if (img.naturalWidth * scale < 16) {
    scale = 16 / img.naturalWidth;
  }

  // apply the new scale to the native offset
  deltaX = e.clientX - xoff * scale;
  deltaY = e.clientY - yoff * scale;

  // now modify the attributes of the image to reflect the changes
  img.style.top = deltaY + "px";
  img.style.left = deltaX + "px";

  img.style.width = (img.naturalWidth * scale) + "px";
}

window.onresize = function(e) {
  document.getElementById("wrapper").style.width = window.innerWidth + "px";
  document.getElementById("wrapper").style.height = window.innerHeight + "px";
}

window.onload = function(e) {
  document.getElementById("wrapper").style.width = window.innerWidth + "px";
  document.getElementById("wrapper").style.height = window.innerHeight + "px";
}
&#13;
html,
body {
  height: 100%;
  width: 100%;
  margin: 0;
  padding: 0;
  overflow: hidden;
}

div {
  overflow: hidden;
}
&#13;
<div id="wrapper" style="position:relative;">
  <img id="image" style="width:100%;position:absolute;top:0px;left:0px;" src="http://lorempixel.com/output/animals-q-c-1920-1920-9.jpg" alt="">
</div>
&#13;
&#13;
&#13;

答案 2 :(得分:0)

我喜欢fmacdee的这两个帖子。我将他创建的代码考虑成可以在任何图像上调用的可重用版本。

只需致电:

var imageScaler = new ImageScaler(document.getElementById("image")); 
imageScaler.setup();

并在项目的某处包含此代码:

var ImageScaler = function(img)
{ 
    this.img = img; 
    this.scale = this.getImageScale(); 
    this.panning = false; 

    this.start = {x: 0, y: 0};
    this.delta = {x: 0, y: 0}; 
}; 

ImageScaler.prototype = 
{ 
    constructor: ImageScaler, 

    setup: function()
    { 
        this.setupEvents(); 
    }, 

    setupEvents: function()
    { 
        var img = this.img; 
        var callBack = this.onScale.bind(this); 
        var touchDown = this.touchDown.bind(this), 
        touhcMove = this.touchMove.bind(this), 
        touchUp = this.touchUp.bind(this); 

        img.onwheel = callBack; 
        img.onmousedown = touchDown;
        img.onmousemove = touhcMove;
        img.onmouseup = touchUp;
    }, 

    getImageScale: function()
    { 
        var img = this.img; 
        return img.offsetWidth / img.naturalWidth;
    }, 

    getMouseDirection: function(e)
    { 
        return (e.wheelDelta ? e.wheelDelta : -e.deltaY); 
    }, 

    getOffset: function(e)
    { 
        var scale = this.scale, 
        delta = this.delta; 
        // first, remove the scale so we have the native offset
        return {
            x: (e.clientX - delta.x) / scale, 
            y: (e.clientY - delta.y) / scale
        }; 
    }, 

    scaleElement: function(x, y, scale)
    { 
        var img = this.img; 
        img.style.top = y + "px";
        img.style.left = x + "px";
        img.style.width = (img.naturalWidth * scale) + "px";
    }, 

    minScale: 0.2, 

    updateScale: function(delta)
    { 
        // get scroll direction & set zoom level
        var scale = (delta > 0) ? (this.scale *= 1.05) : (this.scale /= 1.05);

        // limit the smallest size so the image does not disappear
        if (scale <= this.minScale) 
        {
            this.scale = this.minScale;
        }
        return this.scale; 
    }, 

    touchDown: function(e)
    { 
        var delta = this.delta; 
        this.start = {x: e.clientX - delta.x, y: e.clientY - delta.y};
        this.panning = true; 
    }, 

    touchMove: function(e)
    { 
        e.preventDefault();         
        if (this.panning === false) 
        {
            return;
        } 

        var delta = this.delta, 
        start = this.start; 
        delta.x = (e.clientX - start.x);
        delta.y = (e.clientY - start.y); 
        console.log(delta, start)

        this.scaleElement(delta.x, delta.y, this.scale);
    }, 

    touchUp: function(e)
    { 
        this.panning = false; 
    }, 

    onScale: function(e)
    { 
        var offset = this.getOffset(e); 
        e.preventDefault(); 

        // get scroll direction & set zoom level
        var delta = this.getMouseDirection(e);
        var scale = this.updateScale(delta); 

        // apply the new scale to the native offset
        delta = this.delta; 
        delta.x = e.clientX - offset.x * scale;
        delta.y = e.clientY - offset.y * scale;

        this.scaleElement(delta.x, delta.y, scale); 
    }
};

我做了一个小提琴来查看结果:http://jsfiddle.net/acqo5n8s/12/