如何制作具有背景位置和百分比值的可拖动背景图像?
How to make a draggable background image with background-position and percentage values?
我正在尝试使用 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;
}
<div></div>
如果我点击背景上的一个白色十字并移动鼠标,那么十字应该始终保持在光标下方,直到我到达图像的末尾或容器的末尾。
这可能只是一道数学题,但我有点困惑,因为百分比与 background-position
的关系。有什么想法吗?
我不知道公式是怎样的,但你也必须包括 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-axis 完成):
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>
或者回答您的 follow-up 问题:
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-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;
}
<div></div>
如果我点击背景上的一个白色十字并移动鼠标,那么十字应该始终保持在光标下方,直到我到达图像的末尾或容器的末尾。
这可能只是一道数学题,但我有点困惑,因为百分比与 background-position
的关系。有什么想法吗?
我不知道公式是怎样的,但你也必须包括 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-axis 完成):
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>
或者回答您的 follow-up 问题:
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>