【问题标题】:How to make a draggable background image with background-position and percentage values?如何制作具有背景位置和百分比值的可拖动背景图像?
【发布时间】:2023-04-11 12:26:03
【问题描述】:

我正在尝试使用background-position 和百分比值制作一个简单的可拖动背景。 到目前为止,我设法让拖动工作,但我似乎无法找到正确的计算方法让图像以相同的速度跟随光标(如果有意义的话)。

这是一个简单的例子(仅使用x 轴):

const container = document.querySelector('div');
const containerSize = container.getBoundingClientRect();

let imagePosition = { x: 50, y: 50 };
let cursorPosBefore = { x: 0, y: 0 };
let imagePosBefore = null;
let imagePosAfter = imagePosition;

container.addEventListener('mousedown', function(event) {
  cursorPosBefore = { x: event.clientX, y: event.clientY };
  imagePosBefore = imagePosAfter; // Get current image position
});

container.addEventListener('mousemove', function(event) {
  if (event.buttons === 0) return;

  let newXPos = imagePosBefore.x + ((cursorPosBefore.x - event.clientX) * 100 / containerSize.width);
  newXPos = (newXPos < 0) ? 0 : (newXPos > 100) ? 100 : newXPos; // Stop at the end of the image
  
  imagePosAfter = { x: newXPos, y: imagePosition.y }; // Save position
  container.style.backgroundPosition = `${newXPos}% ${imagePosition.y}%`;
});
div {
  width: 400px;
  height: 400px;
  background-position: 50% 50%;
  background-size: cover;
  background-repeat: no-repeat;
  background-image: url('https://i.stack.imgur.com/5yqL8.png');
  cursor: move;
  border: 2px solid transparent;
}

div:active {
  border-color: red;
}
&lt;div&gt;&lt;/div&gt;

如果我单击背景上的一个白色十字并移动鼠标,则十字应始终保持在光标下方,直到我到达图像末端或容器末端。

这可能只是一个数学问题,但我有点困惑,因为百分比如何与background-position 一起工作。有什么想法吗?

【问题讨论】:

    标签: javascript css background-position


    【解决方案1】:

    我不知道公式到底是怎样的,但你也必须包括 css-background 的大小。

    如果背景大小是容器 div 大小的 100%,您的公式将有效,但事实并非如此。您需要知道缩放级别(可以根据与 div 大小相关的大小来计算)。

    以下是设置缩放级别的公式:

    container.addEventListener('mousemove', function(event) {
        event.preventDefault();
    
        const zoomAffector = (currentZoomLevel - 100) / 100; // 100% zoom = image as big as container
        if (zoomAffector <= 0) return; // Cant drag a image that is zoomed out
    
        let newXPos = imagePosBefore.x + ((imagePosBefore.x - event.pageX) / zoomAffector * 100;);
        newXPos = (newXPos < 0) ? 0 : (newXPos > 100) ? 100 : newXPos;
      
        imagePosAfter = { x: newXPos, y: imagePosition.y };
        container.style.backgroundPosition = `${newXPos}% ${imagePosition.y}%`;
    });
    

    要获取 css-background 图像的大小,可以查看这个问题:Get the Size of a CSS Background Image Using JavaScript?

    这是我对您的问题的尝试,使用链接问题中的一个答案来获取图像宽度(对于 y 轴也这样做,以完成):

    const container = document.querySelector('div');
    const containerSize = container.getBoundingClientRect();
    
    let imagePosition = { x: 50, y: 50 };
    let cursorPosBefore = { x: 0, y: 0 };
    let imagePosBefore = null;
    let imagePosAfter = imagePosition;
    
    
    var actualImage = new Image();
    actualImage.src = $('#img').css('background-image').replace(/"/g,"").replace(/url\(|\)$/ig, "");
    actualImage.onload = function() {
        const zoomX = this.width / containerSize.width - 1;
        const zoomY = this.height / containerSize.height - 1;
        
        container.addEventListener('mousedown', function(event) {
          cursorPosBefore = { x: event.clientX, y: event.clientY };
          imagePosBefore = imagePosAfter; // Get current image position
        });
    
        container.addEventListener('mousemove', function(event) {
            event.preventDefault();
    
            if (event.buttons === 0) return;
    
            let newXPos = imagePosBefore.x + ((cursorPosBefore.x - event.clientX) / containerSize.width * 100 / zoomX);
            newXPos = (newXPos < 0) ? 0 : (newXPos > 100) ? 100 : newXPos;
            let newYPos = imagePosBefore.y + ((cursorPosBefore.y - event.clientY) / containerSize.height * 100 / zoomY);
            newYPos = (newYPos < 0) ? 0 : (newYPos > 100) ? 100 : newYPos;
    
            imagePosAfter = { x: newXPos, y: newYPos };
            container.style.backgroundPosition = `${newXPos}% ${newYPos}%`;
        });
    }
    #img {
      width: 400px;
      height: 200px;
      background-position: 50% 50%;
      background-image: url('https://i.stack.imgur.com/5yqL8.png');
      cursor: move;
      border: 2px solid transparent;
    }
    
    #img:active {
      border-color: red;
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
    <div id="img"></div>

    或者更干净一点:

    const container = document.querySelector('div');
    const containerSize = container.getBoundingClientRect();
    
    let imagePosition = { x: 50, y: 50 };
    let cursorPosBefore = { x: 0, y: 0 };
    let imagePosBefore = null;
    let imagePosAfter = imagePosition;
    
    // Helpers
    const minMax = (pos) => (pos < 0) ? 0 : (pos > 100) ? 100 : pos;
    const setNewCenter = (x, y) => {
      imagePosAfter = { x: x, y: y }; 
      container.style.backgroundPosition = `${x}% ${y}%`;
    };
    
    const getImageZoom = () => {
      return new Promise((resolve, reject) => {
        let actualImage = new Image();
        actualImage.src = $('#img').css('background-image').replace(/"/g,"").replace(/url\(|\)$/ig, "");
        actualImage.onload = function() {
          resolve({
            x: zoomX = this.width / containerSize.width - 1,
            y: zoomY = this.height / containerSize.height - 1
          });
        }
      });
    }
        
    const addEventListeners = (zoomLevels) => {container.addEventListener('mousedown', function(event) {
          cursorPosBefore = { x: event.clientX, y: event.clientY };
          imagePosBefore = imagePosAfter; // Get current image position
        });
    
        container.addEventListener('mousemove', function(event) {
            event.preventDefault();
    
            if (event.buttons === 0) return;
    
            let newXPos = imagePosBefore.x + ((cursorPosBefore.x - event.clientX) / containerSize.width * 100 / zoomLevels.x);
            let newYPos = imagePosBefore.y + ((cursorPosBefore.y - event.clientY) / containerSize.height * 100 / zoomLevels.y);
    
            setNewCenter(minMax(newXPos), minMax(newYPos));
        });
    };
    
    getImageZoom().then(zoom => addEventListeners(zoom));
        
    #img {
      width: 400px;
      height: 200px;
      background-position: 50% 50%;
      background-image: url('https://i.stack.imgur.com/5yqL8.png');
      cursor: move;
      border: 2px solid transparent;
    }
    
    #img:active {
      border-color: red;
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
    <div id="img"></div>

    或回答您的后续问题:

    const container = document.querySelector("div");
    const containerSize = container.getBoundingClientRect();
    
    let imagePosition = { x: 50, y: 50 };
    let cursorPosBefore = { x: 0, y: 0 };
    let imagePosBefore = null;
    let imagePosAfter = imagePosition;
    
    // Helpers
    const minMax = (pos) => (pos < 0 ? 0 : pos > 100 ? 100 : pos);
    const setNewCenter = (x, y) => {
      imagePosAfter = { x: x, y: y };
      container.style.backgroundPosition = `${x}% ${y}%`;
    };
    
    const getImageZoom = () => {
      return new Promise((resolve, reject) => {
        let actualImage = new Image();
    
        actualImage.src = $("#img")
          .css("background-image")
          .replace(/"/g, "")
          .replace(/url\(|\)$/gi, "");
        actualImage.onload = function () {
          const imgW = this.width,
            imgH = this.height,
            conW = containerSize.width,
            conH = containerSize.height,
            ratioW = imgW / conW,
            ratioH = imgH / conH;
    
          // Stretched to Height
          if (ratioH < ratioW) {
            resolve({
              x: imgW / (conW * ratioH) - 1,
              y: imgH / (conH * ratioH) - 1,
            });
          } else {
            // Stretched to Width
            resolve({
              x: imgW / (conW * ratioW) - 1,
              y: imgH / (conH * ratioW) - 1,
            });
          }
        };
      });
    };
    
    const addEventListeners = (zoomLevels) => {
      container.addEventListener("mousedown", function (event) {
        cursorPosBefore = { x: event.clientX, y: event.clientY };
        imagePosBefore = imagePosAfter; // Get current image position
      });
    
      container.addEventListener("mousemove", function (event) {
        event.preventDefault();
    
        if (event.buttons === 0) return;
    
        let newXPos =
          imagePosBefore.x +
          (((cursorPosBefore.x - event.clientX) / containerSize.width) * 100) /
            zoomLevels.x;
        let newYPos =
          imagePosBefore.y +
          (((cursorPosBefore.y - event.clientY) / containerSize.height) * 100) /
            zoomLevels.y;
    
        setNewCenter(minMax(newXPos), minMax(newYPos));
      });
    };
    
    getImageZoom().then((zoom) => addEventListeners(zoom));
    #img {
      width: 400px;
      height: 200px;
      background-size: cover;
      background-position: 50% 50%;
      background-image: url('https://i.stack.imgur.com/5yqL8.png');
      cursor: move;
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
    <div id="img"></div>

    【讨论】:

    • 糟糕,我忘记在示例中添加background-size 属性(我编辑了问题以添加它),图像采用容器的大小。我会检查你的代码,谢谢。 (:
    • 背景大小:conver 没有区别。示例仍然有效。 @Arkellys 您仍然需要注意缩放。
    • 是的,这绝对是我忘记的参数!我无法正确测试您的代码,所以如果它有效,我稍后会回来接受您的 anwser,再次感谢!
    • 没问题。 @Arkellys
    • 嗯.. 我可能接受得太早了,它不适用于将background-size 设置为cover
    猜你喜欢
    • 2018-10-13
    • 1970-01-01
    • 2017-12-30
    • 2017-05-07
    • 1970-01-01
    • 2014-02-14
    • 2014-02-27
    • 1970-01-01
    • 2015-09-26
    相关资源
    最近更新 更多