#javascript #vue.js
#javascript #vue.js
Вопрос:
у меня есть компонент Vue, который позволяет мне увеличивать изображение, с помощью колесика мыши работает нормально, однако увеличенный масштаб ведет себя странно.
В принципе, если вы поместите 2 пальца на экран далеко друг от друга, это сильно увеличит масштаб, поместите их ближе друг к другу, и это немного увеличит масштаб. Что должно произойти, так это то, что он не увеличивает или уменьшает масштаб, пока вы на самом деле не переместите пальцы внутрь или наружу.
Есть идеи?
Vue.component('test', {
data() {
return {
loading: false,
loop: true,
speed: 8,
speedController: 0,
zoomEnabled: true,
zoomLevels: [1, 1.5, 2, 2.5, 3],
zoomLevel: 1,
frame: 1,
images: [],
imagesPreloaded: 0,
spinEnabled: true,
spinAuto: false,
reverse: false,
viewportScale: 0.3,
viewportEnabled: true,
viewportOpacity: 0.8,
lastX: 0,
lastY: 0,
startX: 0,
startY: 0,
translateX: 0,
translateY: 0,
isMoving: false,
isDragging: false,
lastPinch: 0,
animationRequestID: 0,
spinStart: null,
spinThen: Date.now(),
fps: 1000 / 8,
axiosRequest: null,
$clickEvent: null,
$moveEvent: null,
output: ''
};
},
mounted() {
window.addEventListener('mouseup', this.handleEnd);
window.addEventListener('touchend', this.handleEnd);
window.addEventListener('resize', this.fetch);
},
beforeDestroy() {
window.removeEventListener('mouseup', this.handleEnd);
window.removeEventListener('touchend', this.handleEnd);
},
methods: {
handleSlider(event) {
this.frame = Number(event.target.value);
},
zoom(direction) {
if (this.zoomLevels[this.zoomLevels.indexOf(this.closestZoom) direction] === undefined) {
return;
}
let current = this.zoomLevels.indexOf(this.closestZoom);
let index = current = direction;
if (direction === 0) {
index = 0;
}
this.zoomLevel = this.zoomLevels[index];
this.translate(null, true);
},
zoomWheel($event) {
this.zoomLevel = $event.deltaY * -0.01;
if (this.zoomLevel < 1) {
this.zoomLevel = 1;
}
$event.preventDefault();
let maxZoom = this.zoomLevels[this.zoomLevels.length - 1];
this.zoomLevel = Math.min(Math.max(.125, this.zoomLevel), maxZoom);
this.translate(null, true);
},
zoomPinch($event) {
let curDiff = Math.abs($event.touches[0].clientX - $event.touches[1].clientX);
$event.deltaY = this.lastPinch - curDiff;
this.zoomWheel($event);
this.lastPinch = curDiff;
},
handleStart($event) {
if ($event.button amp;amp; $event.button !== 0) {
return;
}
this.$clickEvent = $event;
if (this.animationRequestID !== 0) {
this.spinStop();
}
this.isMoving = true;
this.isDragging = true;
// this.startTouchX = [ $event.touches[0].clientX, $event.touches[1].clientX ];
// this.startTouchY = [ [ $event.touches[0].clientY, $event.touches[1].clientY ] ];
this.startX = this.$clickEvent.pageX || this.$clickEvent.touches[0].pageX;
this.startY = this.$clickEvent.pageY || this.$clickEvent.touches[0].pageY;
},
handleMove($event, viewport) {
if ($event.button amp;amp; $event.button !== 0) {
return;
}
if ($event.touches amp;amp; $event.touches.length > 1) {
this.zoomPinch($event);
return;
}
this.$moveEvent = $event;
if (this.isMoving amp;amp; this.isDragging) {
const positions = {
x: $event.pageX || $event.touches[0].pageX,
y: $event.pageY || $event.touches[0].pageY
}
if (this.zoomLevel !== 1) {
this.translate(positions, null, viewport);
}
if (this.zoomLevel === 1) {
this.changeFrame(positions);
}
this.lastX = positions.x;
this.lastY = positions.y;
}
},
handleEnd($event) {
if ($event.button amp;amp; $event.button !== 0) {
return;
}
this.isMoving = false;
},
spin(index) {
let i = index;
if (i >= this.images.length) {
i = 1;
}
this.animationRequestID = window.requestAnimationFrame(() => this.spin(i));
let now = Date.now();
let elapsed = now - this.spinThen;
if (elapsed > this.fps) {
this.spinThen = now - (elapsed % this.fps);
this.frame = i;
i = 1;
}
},
spinToggle() {
if (this.animationRequestID === 0 amp;amp; this.zoomLevel === 1) {
this.spin(this.frame);
return;
}
this.spinStop();
},
spinStop() {
if (this.animationRequestID) {
window.cancelAnimationFrame(this.animationRequestID);
this.animationRequestID = 0;
}
},
translate(positions, zooming, viewport) {
if (this.$moveEvent) {
this.$moveEvent.preventDefault();
}
window.requestAnimationFrame(() => {
positions = positions || {
x: this.startX,
y: this.startY
};
if (viewport) {
this._translateFromViewport(positions);
} else {
this._translateFromImage(positions, zooming);
}
this.startX = positions.x;
this.startY = positions.y;
});
},
/**
* @param positions
* @private
*/
_translateFromViewport: function(positions) {
let move = {
x: Math.floor(positions.x - this.startX),
y: Math.floor(positions.y - this.startY)
};
let box = this.$refs.viewportBox.getBoundingClientRect();
let container = this.$refs.viewportContainer.getBoundingClientRect();
// Amount of pixels moved within animation frame, adjust based on viewport scale.
// Zoom level doesn't matter as image scale doesn't move, so box is moving same amount of pixels.
let moveAmountX = (move.x / this.viewportScale);
let moveAmountY = (move.y / this.viewportScale);
// Find the current offset of the container bounds, calculate new offset based on movement amount
let calculatedOffset = {
left: (container.left - box.left) - moveAmountX,
right: (container.right - box.right) - moveAmountX,
top: (container.top - box.top) - moveAmountY,
bottom: (container.bottom - box.bottom) - moveAmountY
};
this.output = JSON.stringify(calculatedOffset);
// Only move if the calculated new offset is not out of container bounds
// Reverse the movement as moving box in same direction as cursor rather than the image.
if (calculatedOffset.left <= 0 amp;amp; calculatedOffset.right >= 0) {
this.translateX = -moveAmountX;
}
if (calculatedOffset.top <= 0 amp;amp; calculatedOffset.bottom >= 0) {
this.translateY = -moveAmountY;
}
},
_translateFromImage: function(positions, zooming) {
let move = {
x: Math.floor(positions.x - this.startX),
y: Math.floor(positions.y - this.startY)
};
let image = this.$refs.image.getBoundingClientRect();
let container = this.$refs.container.getBoundingClientRect();
let moveAmountX = move.x * this.zoomLevel;
let moveAmountY = move.y * this.zoomLevel;
let calculatedOffset = {
left: (container.left - image.left) - moveAmountX,
right: (container.right - image.right) - moveAmountX,
top: (container.top - image.top) - moveAmountY,
bottom: (container.bottom - image.bottom) - moveAmountY
};
if (zooming) {
if (calculatedOffset.left <= 0) {
this.translateX = calculatedOffset.left;
}
if (calculatedOffset.right >= 0) {
this.translateX = calculatedOffset.right;
}
if (calculatedOffset.top <= 0) {
this.translateY = calculatedOffset.top;
}
if (calculatedOffset.bottom >= 0) {
this.translateY = calculatedOffset.bottom;
}
}
if (calculatedOffset.left >= 0 amp;amp; calculatedOffset.right <= 0) {
this.translateX = move.x / this.zoomLevel;
}
if (calculatedOffset.top >= 0 amp;amp; calculatedOffset.bottom <= 0) {
this.translateY = move.y / this.zoomLevel;
}
},
changeFrame(positions) {
this.speedController = 1;
if (this.speedController < this.speed) {
return;
}
if (this.speedController > this.speed) {
this.speedController = 0;
}
if (positions.x > this.lastX) {
if (this.frame >= 0 amp;amp; this.frame < this.images.length) {
this.frame = 1;
} else if (this.loop) {
this.frame = 1;
}
} else if (positions.x < this.lastX) {
if (this.frame >= 0 amp;amp; this.frame - 1 > 0) {
this.frame -= 1;
} else if (this.loop) {
this.frame = this.images.length;
}
}
}
},
watch: {
zoomLevel: function() {
if (this.zoomLevel !== 1 amp;amp; this.animationRequestID !== 0) {
this.spinStop();
}
}
},
computed: {
closestZoom: function() {
return this.zoomLevels.reduce((a, b) => {
return Math.abs(b - this.zoomLevel) < Math.abs(a - this.zoomLevel) ? b : a;
});
},
imageSet: function() {
return this.images.map(image => {
return image[this.closestZoom].url;
});
},
preloadProgress: function() {
return Math.floor(this.imagesPreloaded / this.images.length * 100);
},
currentPath: function() {
return this.images[this.frame - 1][this.closestZoom].url;
},
nextZoomLevel: function() {
if (this.zoomLevels.indexOf(this.closestZoom) === this.zoomLevels.length - 1) {
return this.zoomLevels[0];
}
return this.zoomLevels[this.zoomLevels.indexOf(this.closestZoom) 1];
},
viewportTransform: function() {
if (this.viewportEnabled) {
let translateX = -((this.translateX * this.viewportScale) * this.zoomLevel);
let translateY = -((this.translateY * this.viewportScale) * this.zoomLevel);
return `scale(${1 / this.zoomLevel}) translateX(${translateX}px) translateY(${translateY}px)`;
}
},
transform: function() {
return `scale(${this.zoomLevel}) translateX(${this.translateX}px) translateY(${this.translateY}px)`;
},
canZoomIn: function() {
return this.zoomLevels[this.zoomLevels.indexOf(this.closestZoom) 1] === undefined
},
canZoomOut: function() {
return this.zoomLevels[this.zoomLevels.indexOf(this.closestZoom) -1] === undefined
}
},
template: '#template'
});
window.vue = new Vue({}).$mount('#app');
.media-360-viewer {
position: relative;
overflow: hidden;
display: inline-block;
background: #000;
width: 100%;
transition: filter .2s ease-in-out;
amp;__image {
width: 100%;
cursor: grab;
amp;.isTranslating {
cursor: grabbing;
}
amp;.loading {
filter: blur(4px);
}
}
amp;__loader {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, .5);
* {
user-select: none;
}
amp;>svg {
width: 100%;
height: 100%;
transform: rotate(270deg);
}
amp;--text {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
p {
font-size: 100%;
font-weight: bold;
color: #fff;
amp;.large {
font-size: 150%;
}
}
}
amp;--background {
stroke-dasharray: 0;
stroke-dashoffset: 0;
stroke: rgba(0, 0, 0, .7);
stroke-width: 25px;
}
amp;--cover {
stroke-dasharray: 200%;
stroke: #848484;
stroke-width: 15px;
stroke-linecap: round;
}
amp;--background,
amp;--cover {
fill: transparent;
}
}
amp;__viewport {
position: absolute;
top: 10px;
left: 10px;
z-index: 2;
overflow: hidden;
amp;--image {
width: 100%;
pointer-events: none;
}
amp;--zoom {
position: absolute;
bottom: 5px;
right: 5px;
color: #fff;
z-index: 3;
font-size: 12px;
pointer-events: none;
}
amp;--square {
display: block;
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
box-shadow: rgba(0, 0, 0, .6) 0 0 0 10000px;
cursor: grab;
transition: background ease-in-out .1s;
amp;:hover {
background: rgba(255, 255, 255, .2);
}
}
}
amp;__tools {
position: absolute;
bottom: 0;
left: 0;
width: 100%;
display: flex;
align-items: center;
justify-content: center;
padding-bottom: 10px;
amp;>a {
margin: 0 5px;
color: #000;
background: #fff;
border-radius: 50%;
width: 40px;
text-align: center;
line-height: 40px;
amp;[disabled] {
opacity: .5;
cursor: not-allowed;
amp;:hover {
color: #000;
background: #fff;
}
}
amp;:hover {
background: #000;
color: #fff;
}
}
amp;--autoplay {
amp;:before {
font-family: 'ClickIcons';
content: 'ea81';
}
amp;.active:before {
content: 'eb48';
}
}
}
}
.fade-enter-active,
.fade-leave-active {
transition: opacity .5s;
}
.fade-enter,
.fade-leave-to {
opacity: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<test class="test"></test>
</div>
<script type="text/x-template" id="template">
<div>
<div class="media-360-viewer" ref="container">
<img tabindex="1" ref="image" draggable="false" src="https://s3-eu-west-1.amazonaws.com/crash.net/visordown.com/styles/amp_1200/s3/2020_YAM_YZF1000R1SPL_EU_BWM2_STA_001-70560.jpg?itok=5bisLKmj" :style="{ transform: transform }" class="media-360-viewer__image"
@touchend="handleEnd" @touchmove="handleMove" @touchstart="handleStart" @wheel="zoomWheel" alt="360 Image" />
</div>
</div>
</script>
Ответ №1:
Я не уверен, что правильно понимаю вашу проблему, но, как я пытаюсь, я думаю, что проблема вашего кода в этой строке:
$event.deltaY = this.lastPinch - curDiff;
Кажется, что this.lastPinch
удерживается дельта предыдущего touchmove
события, поэтому при первом событии вы должны игнорировать его и уточнить, когда touchend
.
...
zoomPinch ($event) {
...
if (this.lastPinch) {
$event.deltaY = this.lastPinch - curDiff;
this.zoomWheel($event);
}
...
}
...
handleEnd ($event) {
...
this.lastPinch = 0
}
...
Vue.component('test', {
data() {
return {
loading: false,
loop: true,
speed: 8,
speedController: 0,
zoomEnabled: true,
zoomLevels: [1, 1.5, 2, 2.5, 3],
zoomLevel: 1,
frame: 1,
images: [],
imagesPreloaded: 0,
spinEnabled: true,
spinAuto: false,
reverse: false,
viewportScale: 0.3,
viewportEnabled: true,
viewportOpacity: 0.8,
lastX: 0,
lastY: 0,
startX: 0,
startY: 0,
translateX: 0,
translateY: 0,
isMoving: false,
isDragging: false,
lastPinch: 0,
animationRequestID: 0,
spinStart: null,
spinThen: Date.now(),
fps: 1000 / 8,
axiosRequest: null,
$clickEvent: null,
$moveEvent: null,
output: ''
};
},
mounted() {
window.addEventListener('mouseup', this.handleEnd);
window.addEventListener('touchend', this.handleEnd);
window.addEventListener('resize', this.fetch);
},
beforeDestroy() {
window.removeEventListener('mouseup', this.handleEnd);
window.removeEventListener('touchend', this.handleEnd);
},
methods: {
handleSlider(event) {
this.frame = Number(event.target.value);
},
zoom(direction) {
if (this.zoomLevels[this.zoomLevels.indexOf(this.closestZoom) direction] === undefined) {
return;
}
let current = this.zoomLevels.indexOf(this.closestZoom);
let index = current = direction;
if (direction === 0) {
index = 0;
}
this.zoomLevel = this.zoomLevels[index];
this.translate(null, true);
},
zoomWheel($event) {
this.zoomLevel = $event.deltaY * -0.01;
if (this.zoomLevel < 1) {
this.zoomLevel = 1;
}
$event.preventDefault();
let maxZoom = this.zoomLevels[this.zoomLevels.length - 1];
this.zoomLevel = Math.min(Math.max(.125, this.zoomLevel), maxZoom);
this.translate(null, true);
},
zoomPinch($event) {
let curDiff = Math.abs($event.touches[0].clientX - $event.touches[1].clientX);
if (this.lastPinch) {
$event.deltaY = this.lastPinch - curDiff;
this.zoomWheel($event);
}
this.lastPinch = curDiff;
},
handleStart($event) {
if ($event.button amp;amp; $event.button !== 0) {
return;
}
this.$clickEvent = $event;
if (this.animationRequestID !== 0) {
this.spinStop();
}
this.isMoving = true;
this.isDragging = true;
// this.startTouchX = [ $event.touches[0].clientX, $event.touches[1].clientX ];
// this.startTouchY = [ [ $event.touches[0].clientY, $event.touches[1].clientY ] ];
this.startX = this.$clickEvent.pageX || this.$clickEvent.touches[0].pageX;
this.startY = this.$clickEvent.pageY || this.$clickEvent.touches[0].pageY;
},
handleMove($event, viewport) {
if ($event.button amp;amp; $event.button !== 0) {
return;
}
if ($event.touches amp;amp; $event.touches.length > 1) {
this.zoomPinch($event);
return;
}
this.$moveEvent = $event;
if (this.isMoving amp;amp; this.isDragging) {
const positions = {
x: $event.pageX || $event.touches[0].pageX,
y: $event.pageY || $event.touches[0].pageY
}
if (this.zoomLevel !== 1) {
this.translate(positions, null, viewport);
}
if (this.zoomLevel === 1) {
this.changeFrame(positions);
}
this.lastX = positions.x;
this.lastY = positions.y;
}
},
handleEnd($event) {
if ($event.button amp;amp; $event.button !== 0) {
return;
}
this.isMoving = false;
this.lastPinch = 0;
},
spin(index) {
let i = index;
if (i >= this.images.length) {
i = 1;
}
this.animationRequestID = window.requestAnimationFrame(() => this.spin(i));
let now = Date.now();
let elapsed = now - this.spinThen;
if (elapsed > this.fps) {
this.spinThen = now - (elapsed % this.fps);
this.frame = i;
i = 1;
}
},
spinToggle() {
if (this.animationRequestID === 0 amp;amp; this.zoomLevel === 1) {
this.spin(this.frame);
return;
}
this.spinStop();
},
spinStop() {
if (this.animationRequestID) {
window.cancelAnimationFrame(this.animationRequestID);
this.animationRequestID = 0;
}
},
translate(positions, zooming, viewport) {
if (this.$moveEvent) {
this.$moveEvent.preventDefault();
}
window.requestAnimationFrame(() => {
positions = positions || {
x: this.startX,
y: this.startY
};
if (viewport) {
this._translateFromViewport(positions);
} else {
this._translateFromImage(positions, zooming);
}
this.startX = positions.x;
this.startY = positions.y;
});
},
/**
* @param positions
* @private
*/
_translateFromViewport: function(positions) {
let move = {
x: Math.floor(positions.x - this.startX),
y: Math.floor(positions.y - this.startY)
};
let box = this.$refs.viewportBox.getBoundingClientRect();
let container = this.$refs.viewportContainer.getBoundingClientRect();
// Amount of pixels moved within animation frame, adjust based on viewport scale.
// Zoom level doesn't matter as image scale doesn't move, so box is moving same amount of pixels.
let moveAmountX = (move.x / this.viewportScale);
let moveAmountY = (move.y / this.viewportScale);
// Find the current offset of the container bounds, calculate new offset based on movement amount
let calculatedOffset = {
left: (container.left - box.left) - moveAmountX,
right: (container.right - box.right) - moveAmountX,
top: (container.top - box.top) - moveAmountY,
bottom: (container.bottom - box.bottom) - moveAmountY
};
this.output = JSON.stringify(calculatedOffset);
// Only move if the calculated new offset is not out of container bounds
// Reverse the movement as moving box in same direction as cursor rather than the image.
if (calculatedOffset.left <= 0 amp;amp; calculatedOffset.right >= 0) {
this.translateX = -moveAmountX;
}
if (calculatedOffset.top <= 0 amp;amp; calculatedOffset.bottom >= 0) {
this.translateY = -moveAmountY;
}
},
_translateFromImage: function(positions, zooming) {
let move = {
x: Math.floor(positions.x - this.startX),
y: Math.floor(positions.y - this.startY)
};
let image = this.$refs.image.getBoundingClientRect();
let container = this.$refs.container.getBoundingClientRect();
let moveAmountX = move.x * this.zoomLevel;
let moveAmountY = move.y * this.zoomLevel;
let calculatedOffset = {
left: (container.left - image.left) - moveAmountX,
right: (container.right - image.right) - moveAmountX,
top: (container.top - image.top) - moveAmountY,
bottom: (container.bottom - image.bottom) - moveAmountY
};
if (zooming) {
if (calculatedOffset.left <= 0) {
this.translateX = calculatedOffset.left;
}
if (calculatedOffset.right >= 0) {
this.translateX = calculatedOffset.right;
}
if (calculatedOffset.top <= 0) {
this.translateY = calculatedOffset.top;
}
if (calculatedOffset.bottom >= 0) {
this.translateY = calculatedOffset.bottom;
}
}
if (calculatedOffset.left >= 0 amp;amp; calculatedOffset.right <= 0) {
this.translateX = move.x / this.zoomLevel;
}
if (calculatedOffset.top >= 0 amp;amp; calculatedOffset.bottom <= 0) {
this.translateY = move.y / this.zoomLevel;
}
},
changeFrame(positions) {
this.speedController = 1;
if (this.speedController < this.speed) {
return;
}
if (this.speedController > this.speed) {
this.speedController = 0;
}
if (positions.x > this.lastX) {
if (this.frame >= 0 amp;amp; this.frame < this.images.length) {
this.frame = 1;
} else if (this.loop) {
this.frame = 1;
}
} else if (positions.x < this.lastX) {
if (this.frame >= 0 amp;amp; this.frame - 1 > 0) {
this.frame -= 1;
} else if (this.loop) {
this.frame = this.images.length;
}
}
}
},
watch: {
zoomLevel: function() {
if (this.zoomLevel !== 1 amp;amp; this.animationRequestID !== 0) {
this.spinStop();
}
}
},
computed: {
closestZoom: function() {
return this.zoomLevels.reduce((a, b) => {
return Math.abs(b - this.zoomLevel) < Math.abs(a - this.zoomLevel) ? b : a;
});
},
imageSet: function() {
return this.images.map(image => {
return image[this.closestZoom].url;
});
},
preloadProgress: function() {
return Math.floor(this.imagesPreloaded / this.images.length * 100);
},
currentPath: function() {
return this.images[this.frame - 1][this.closestZoom].url;
},
nextZoomLevel: function() {
if (this.zoomLevels.indexOf(this.closestZoom) === this.zoomLevels.length - 1) {
return this.zoomLevels[0];
}
return this.zoomLevels[this.zoomLevels.indexOf(this.closestZoom) 1];
},
viewportTransform: function() {
if (this.viewportEnabled) {
let translateX = -((this.translateX * this.viewportScale) * this.zoomLevel);
let translateY = -((this.translateY * this.viewportScale) * this.zoomLevel);
return `scale(${1 / this.zoomLevel}) translateX(${translateX}px) translateY(${translateY}px)`;
}
},
transform: function() {
return `scale(${this.zoomLevel}) translateX(${this.translateX}px) translateY(${this.translateY}px)`;
},
canZoomIn: function() {
return this.zoomLevels[this.zoomLevels.indexOf(this.closestZoom) 1] === undefined
},
canZoomOut: function() {
return this.zoomLevels[this.zoomLevels.indexOf(this.closestZoom) -1] === undefined
}
},
template: '#template'
});
window.vue = new Vue({}).$mount('#app');
.media-360-viewer {
position: relative;
overflow: hidden;
display: inline-block;
background: #000;
width: 100%;
transition: filter .2s ease-in-out;
amp;__image {
width: 100%;
cursor: grab;
amp;.isTranslating {
cursor: grabbing;
}
amp;.loading {
filter: blur(4px);
}
}
amp;__loader {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, .5);
* {
user-select: none;
}
amp;>svg {
width: 100%;
height: 100%;
transform: rotate(270deg);
}
amp;--text {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
p {
font-size: 100%;
font-weight: bold;
color: #fff;
amp;.large {
font-size: 150%;
}
}
}
amp;--background {
stroke-dasharray: 0;
stroke-dashoffset: 0;
stroke: rgba(0, 0, 0, .7);
stroke-width: 25px;
}
amp;--cover {
stroke-dasharray: 200%;
stroke: #848484;
stroke-width: 15px;
stroke-linecap: round;
}
amp;--background,
amp;--cover {
fill: transparent;
}
}
amp;__viewport {
position: absolute;
top: 10px;
left: 10px;
z-index: 2;
overflow: hidden;
amp;--image {
width: 100%;
pointer-events: none;
}
amp;--zoom {
position: absolute;
bottom: 5px;
right: 5px;
color: #fff;
z-index: 3;
font-size: 12px;
pointer-events: none;
}
amp;--square {
display: block;
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
box-shadow: rgba(0, 0, 0, .6) 0 0 0 10000px;
cursor: grab;
transition: background ease-in-out .1s;
amp;:hover {
background: rgba(255, 255, 255, .2);
}
}
}
amp;__tools {
position: absolute;
bottom: 0;
left: 0;
width: 100%;
display: flex;
align-items: center;
justify-content: center;
padding-bottom: 10px;
amp;>a {
margin: 0 5px;
color: #000;
background: #fff;
border-radius: 50%;
width: 40px;
text-align: center;
line-height: 40px;
amp;[disabled] {
opacity: .5;
cursor: not-allowed;
amp;:hover {
color: #000;
background: #fff;
}
}
amp;:hover {
background: #000;
color: #fff;
}
}
amp;--autoplay {
amp;:before {
font-family: 'ClickIcons';
content: 'ea81';
}
amp;.active:before {
content: 'eb48';
}
}
}
}
.fade-enter-active,
.fade-leave-active {
transition: opacity .5s;
}
.fade-enter,
.fade-leave-to {
opacity: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<test class="test"></test>
</div>
<script type="text/x-template" id="template">
<div>
<div class="media-360-viewer" ref="container">
<img tabindex="1" ref="image" draggable="false" src="https://s3-eu-west-1.amazonaws.com/crash.net/visordown.com/styles/amp_1200/s3/2020_YAM_YZF1000R1SPL_EU_BWM2_STA_001-70560.jpg?itok=5bisLKmj" :style="{ transform: transform }" class="media-360-viewer__image"
@touchend="handleEnd" @touchmove="handleMove" @touchstart="handleStart" @wheel="zoomWheel" alt="360 Image" />
</div>
</div>
</script>
Комментарии:
1. Спасибо, это устранило мою проблему. Я тестировал раньше, и это не сработало, но я забыл скомпилировать код! Но теперь это работает, я скомпилировал!