I have got a Vue component where i can zoom in on an image and then move it around the container. When zoomed there is also a small viewport to show what part of the image is visible. However when moving the image around it is moving faster than the mouse, i'm guessing this is due to using the scale
transform.
I also feel like when i'm clicking and dragging on the viewport I shouldn't be reversing the values twice however this seems to be the only way to get it moving the square with the mouse.
Vue.component('test', {
template: '#template',
data: function() {
return {
loading: true,
loop: true,
speed: 8,
speedController: 0,
zoomEnabled: true,
zoomLevels: [1, 1.5, 2, 2.5, 3],
zoomLevel: 1,
frame: 1,
images: [],
imagesPreloaded: 0,
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,
};
},
mounted() {
window.addEventListener('mouseup', this.handleEnd);
window.addEventListener('touchend', this.handleEnd);
},
beforeDestroy() {
window.removeEventListener('mouseup', this.handleEnd);
window.removeEventListener('touchend', this.handleEnd);
},
methods: {
handleSlider(event) {
this.frame = Number(event.target.value);
},
zoom(direction) {
const closest = this.zoomLevels.reduce((a, b) => {
return Math.abs(b - this.zoomLevel) < Math.abs(a - this.zoomLevel) ? b : a;
});
if (this.zoomLevels[this.zoomLevels.indexOf(closest) + direction] === undefined) {
return;
}
let current = this.zoomLevels.indexOf(closest);
let index = current += direction;
if (direction === 0) {
index = 0;
}
this.zoomLevel = this.zoomLevels[index];
window.requestAnimationFrame(() => {
this.translate(null, this.$refs.image, true);
});
},
zoomWheel($event) {
$event.preventDefault();
this.zoomLevel += $event.deltaY * -0.01;
if (this.zoomLevel < 1) {
this.zoomLevel = 1;
}
let maxZoom = this.zoomLevels[this.zoomLevels.length - 1];
this.zoomLevel = Math.min(Math.max(.125, this.zoomLevel), maxZoom);
window.requestAnimationFrame(() => {
this.translate(null, this.$refs.image, true);
});
},
handleStart($event) {
$event.preventDefault();
if ($event.button && $event.button !== 0) {
return;
}
this.isMoving = true;
this.isDragging = true;
this.startX = $event.pageX || $event.touches[0].pageX;
this.startY = $event.pageY || $event.touches[0].pageY;
},
handleMove($event, viewport) {
if ($event.button && $event.button !== 0) {
return;
}
if (this.isMoving && 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, $event.target, null, viewport);
}
if (this.zoomLevel === 1) {
this.changeFrame(positions);
}
}
},
handleEnd($event) {
if ($event.button && $event.button !== 0) {
return;
}
this.isMoving = false;
},
translate(positions, element, zooming, viewport) {
if (positions === null) {
positions = {
x: this.startX,
y: this.startY
};
}
let move = {
x: Math.floor(positions.x - this.startX),
y: Math.floor(positions.y - this.startY)
};
// Reverse Mouse Movement
if (viewport) {
move.x = -move.x;
move.y = -move.y;
}
let image = element.getBoundingClientRect();
let container = element.parentNode.getBoundingClientRect();
let translate = {
left: Math.floor((container.left - image.left) - (move.x * this.zoomLevel)),
right: Math.floor((container.right - image.right) - (move.x * this.zoomLevel)),
top: Math.floor((container.top - image.top) - (move.y * this.zoomLevel)),
bottom: Math.floor((container.bottom - image.bottom) - (move.y * this.zoomLevel))
};
// Reverse Translate Movement
if (viewport) {
translate.left = -translate.left;
translate.right = -translate.right;
translate.top = -translate.top;
translate.bottom = -translate.bottom;
}
if (zooming) {
if (translate.left <= 0) {
this.translateX += Math.floor(translate.left);
}
if (translate.right >= 0) {
this.translateX += Math.floor(translate.right);
}
if (translate.top <= 0) {
this.translateY += Math.floor(translate.top);
}
if (translate.bottom >= 0) {
this.translateY += Math.floor(translate.bottom);
}
}
if (translate.left >= 0 && translate.right <= 0) {
this.translateX += Math.floor(move.x);
}
if (translate.top >= 0 && translate.bottom <= 0) {
this.translateY += Math.floor(move.y);
}
this.startX = positions.x;
this.startY = positions.y;
}
},
computed: {
nextZoomLevel: function() {
const closest = this.zoomLevels.reduce((a, b) => {
return Math.abs(b - this.zoomLevel) < Math.abs(a - this.zoomLevel) ? b : a;
});
if (this.zoomLevels.indexOf(closest) === this.zoomLevels.length - 1) {
return this.zoomLevels[0];
}
return this.zoomLevels[this.zoomLevels.indexOf(closest) + 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() {
const closest = this.zoomLevels.reduce((a, b) => {
return Math.abs(b - this.zoomLevel) < Math.abs(a - this.zoomLevel) ? b : a;
});
return this.zoomLevels[this.zoomLevels.indexOf(closest) + 1] === undefined
},
canZoomOut: function() {
const closest = this.zoomLevels.reduce((a, b) => {
return Math.abs(b - this.zoomLevel) < Math.abs(a - this.zoomLevel) ? b : a;
});
return this.zoomLevels[this.zoomLevels.indexOf(closest) + -1] === undefined
}
}
});
window.vue = new Vue({el: '#app'});
.media-360-viewer {
position: relative;
overflow: hidden;
background: #000;
}
.media-360-viewer>img {
width: 100%;
}
.media-360-viewer>img.canTranslate {
cursor: grab;
}
.media-360-viewer>img.isTranslating {
cursor: grabbing;
}
.media-360-viewer>img.canRotate {
cursor: w-resize;
}
.media-360-viewer__loader {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
}
.media-360-viewer__loader * {
user-select: none;
}
.media-360-viewer__loader>svg {
width: 100%;
height: 100%;
transform: rotate(270deg);
}
.media-360-viewer__loader--text {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
}
.media-360-viewer__loader--text p {
font-size: 100%;
font-weight: bold;
color: #fff;
}
.media-360-viewer__loader--text p.large {
font-size: 150%;
}
.media-360-viewer__loader--background {
stroke-dasharray: 0;
stroke-dashoffset: 0;
stroke: rgba(0, 0, 0, 0.7);
stroke-width: 25px;
}
.media-360-viewer__loader--cover {
stroke-dasharray: 200%;
stroke: #848484;
stroke-width: 15px;
stroke-linecap: round;
}
.media-360-viewer__loader--background,
.media-360-viewer__loader--cover {
fill: transparent;
}
.media-360-viewer__viewport {
position: absolute;
top: 10px;
left: 10px;
z-index: 2;
border: 1px solid black;
overflow: hidden;
}
.media-360-viewer__viewport--image {
width: 100%;
pointer-events: none;
}
.media-360-viewer__viewport--zoom {
position: absolute;
bottom: 5px;
right: 5px;
color: #fff;
z-index: 3;
font-size: 12px;
pointer-events: none;
}
.media-360-viewer__viewport--square {
border: 1px solid black;
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
box-shadow: rgba(0, 0, 0, 0.5) 0 0 0 10000px;
cursor: grab;
transition: background ease-in-out 0.1s;
}
.media-360-viewer__viewport--square:hover {
background: rgba(255, 255, 255, 0.2);
}
.media-360-viewer__header {
position: absolute;
top: 10px;
left: 0;
width: 100%;
}
.media-360-viewer__tools {
position: absolute;
bottom: 0;
left: 0;
width: 100%;
display: flex;
align-items: center;
justify-content: center;
padding-bottom: 10px;
}
.media-360-viewer__tools>a {
margin: 0 5px;
color: #000;
background: #fff;
border-radius: 50%;
width: 40px;
text-align: center;
line-height: 40px;
}
.media-360-viewer__tools>a[disabled] {
opacity: .5;
cursor: not-allowed;
}
.media-360-viewer__tools>a[disabled]:hover {
color: #000;
background: #fff;
}
.media-360-viewer__tools>a:hover {
background: #000;
color: #fff;
}
.media-360-viewer__tools--autoplay:before {
font-family: 'ClickIcons';
content: '\ea81';
}
.media-360-viewer__tools--autoplay.active:before {
content: '\eb48';
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<test></test>
</div>
<script type="text/x-template" id="template">
<div class="media-360-viewer" ref="container">
<transition name="fade">
<div class="media-360-viewer__viewport" v-if="zoomLevel > 1 && viewportEnabled" :style="{ width: (viewportScale * 100) + '%' }">
<img tabindex="1" draggable="false" alt="Viewport" class="media-360-viewer__viewport--image" src="https://www.bennetts.co.uk/-/media/bikesocial/2019-september-images/2020-yamaha-yzf-r1-and-r1m-review/2020-yamaha-r1-and-r1m_005.ashx?h=493&w=740&la=en&hash=F97CD240F0DDFA9540E912DCF7F07019017035C6">
<span class="media-360-viewer__viewport--zoom">
x{{ Math.round(zoomLevel * 10) / 10 }}
</span>
<span :style="{ transform: viewportTransform }" @mouseup="handleEnd" @mousedown="handleStart" @mousemove="handleMove($event, true)" @touchstart="handleStart" @touchend="handleEnd" @touchmove="handleMove($event, true)" class="media-360-viewer__viewport--square"></span>
</div>
</transition>
<img tabindex="1" ref="image" draggable="false" src="https://www.bennetts.co.uk/-/media/bikesocial/2019-september-images/2020-yamaha-yzf-r1-and-r1m-review/2020-yamaha-r1-and-r1m_005.ashx?h=493&w=740&la=en&hash=F97CD240F0DDFA9540E912DCF7F07019017035C6" :style="{ transform: transform }" :class="{
canTranslate: zoomLevel > 1 && zoomEnabled,
canRotate: zoomLevel === 1,
isTranslating: zoomLevel > 1 && zoomEnabled && isMoving
}" @mouseup="handleEnd" @mousedown="handleStart" @mousemove="handleMove" @touchstart="handleStart" @touchend="handleEnd" @touchmove="handleMove" @dblclick="zoom" @wheel="zoomWheel" alt="360 Image" />
</div>
</script>
Does anyone know how I can fix this?
Below is the updated code with the changes suggested by the accepted answer, only issue now is the viewport square going out of frame.
Vue.component('test', {
template: '#template',
data: function() {
return {
loading: true,
loop: true,
speed: 8,
speedController: 0,
zoomEnabled: true,
zoomLevels: [1, 1.5, 2, 2.5, 3],
zoomLevel: 1,
frame: 1,
images: [],
imagesPreloaded: 0,
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,
};
},
mounted() {
window.addEventListener('mouseup', this.handleEnd);
window.addEventListener('touchend', this.handleEnd);
},
beforeDestroy() {
window.removeEventListener('mouseup', this.handleEnd);
window.removeEventListener('touchend', this.handleEnd);
},
methods: {
zoom(direction) {
// todo: Load high res image based on zoom level
const closest = this.zoomLevels.reduce((a, b) => {
return Math.abs(b - this.zoomLevel) < Math.abs(a - this.zoomLevel) ? b : a;
});
if (this.zoomLevels[this.zoomLevels.indexOf(closest) + direction] === undefined) {
return;
}
let current = this.zoomLevels.indexOf(closest);
let index = current += direction;
if (direction === 0) {
index = 0;
}
this.zoomLevel = this.zoomLevels[index];
this.translate(null, this.$refs.image, true);
},
zoomWheel($event) {
$event.preventDefault();
this.zoomLevel += $event.deltaY * -0.01;
if (this.zoomLevel < 1) {
this.zoomLevel = 1;
}
let maxZoom = this.zoomLevels[this.zoomLevels.length - 1];
this.zoomLevel = Math.min(Math.max(.125, this.zoomLevel), maxZoom);
this.translate(null, this.$refs.image, true);
},
handleStart($event) {
$event.preventDefault();
if ($event.button && $event.button !== 0) {
return;
}
this.isMoving = true;
this.isDragging = true;
this.startX = $event.pageX || $event.touches[0].pageX;
this.startY = $event.pageY || $event.touches[0].pageY;
},
handleMove($event, viewport) {
if ($event.button && $event.button !== 0) {
return;
}
if (this.isMoving && 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, $event.target, null, viewport);
}
}
},
handleEnd($event) {
if ($event.button && $event.button !== 0) {
return;
}
this.isMoving = false;
},
translate(positions, element, zooming, viewport) {
window.requestAnimationFrame(() => {
positions = positions || {
x: this.startX,
y: this.startY
};
if (viewport) {
this._translateFromViewport(positions, element);
} else {
this._translateFromImage(positions, element, zooming);
}
this.startX = positions.x;
this.startY = positions.y;
});
},
_translateFromViewport: function(positions, element) {
let move = {
x: positions.x - this.startX,
y: positions.y - this.startY
};
let box = element.getBoundingClientRect();
let container = element.parentNode.getBoundingClientRect();
let translate = {
left: (container.left - box.left) - ((move.x * this.viewportScale) * this.zoomLevel),
right: (container.right - box.right) - ((move.x * this.viewportScale) * this.zoomLevel),
top: (container.top - box.top) - ((move.y * this.viewportScale) * this.zoomLevel),
bottom: (container.bottom - box.bottom) - ((move.y * this.viewportScale) * this.zoomLevel)
};
if (translate.left <= 0 && translate.right >= 0) {
this.translateX -= move.x / this.viewportScale;
}
if (translate.top <= 0 && translate.bottom >= 0) {
this.translateY -= move.y / this.viewportScale
}
},
_translateFromImage: function(positions, element, zooming) {
let move = {
x: Math.floor(positions.x - this.startX),
y: Math.floor(positions.y - this.startY)
};
let image = element.getBoundingClientRect();
let container = element.parentNode.getBoundingClientRect();
let translate = {
left: (container.left - image.left) - (move.x * this.zoomLevel),
right: (container.right - image.right) - (move.x * this.zoomLevel),
top: (container.top - image.top) - (move.y * this.zoomLevel),
bottom: (container.bottom - image.bottom) - (move.y * this.zoomLevel)
};
if (zooming) {
if (translate.left <= 0) {
this.translateX += translate.left;
}
if (translate.right >= 0) {
this.translateX += translate.right;
}
if (translate.top <= 0) {
this.translateY += translate.top;
}
if (translate.bottom >= 0) {
this.translateY += translate.bottom;
}
}
if (translate.left >= 0 && translate.right <= 0) {
this.translateX += move.x / this.zoomLevel;
}
if (translate.top >= 0 && translate.bottom <= 0) {
this.translateY += move.y / this.zoomLevel;
}
},
},
computed: {
preloadProgress: function() {
return Math.floor(this.imagesPreloaded / this.images.length * 100);
},
currentPath: function() {
return this.images[this.frame - 1];
},
nextZoomLevel: function() {
const closest = this.zoomLevels.reduce((a, b) => {
return Math.abs(b - this.zoomLevel) < Math.abs(a - this.zoomLevel) ? b : a;
});
if (this.zoomLevels.indexOf(closest) === this.zoomLevels.length - 1) {
return this.zoomLevels[0];
}
return this.zoomLevels[this.zoomLevels.indexOf(closest) + 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() {
const closest = this.zoomLevels.reduce((a, b) => {
return Math.abs(b - this.zoomLevel) < Math.abs(a - this.zoomLevel) ? b : a;
});
return this.zoomLevels[this.zoomLevels.indexOf(closest) + 1] === undefined
},
canZoomOut: function() {
const closest = this.zoomLevels.reduce((a, b) => {
return Math.abs(b - this.zoomLevel) < Math.abs(a - this.zoomLevel) ? b : a;
});
return this.zoomLevels[this.zoomLevels.indexOf(closest) + -1] === undefined
}
}
});
window.vue = new Vue({
el: '#app'
});
.media-360-viewer {
position: relative;
overflow: hidden;
background: #000;
width: 500px;
}
.media-360-viewer__image {
width: 100%;
}
.media-360-viewer__image.canTranslate {
cursor: grab;
}
.media-360-viewer__image.isTranslating {
cursor: grabbing;
}
.media-360-viewer__image.canRotate {
cursor: w-resize;
}
.media-360-viewer__loader {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
}
.media-360-viewer__loader * {
user-select: none;
}
.media-360-viewer__loader > svg {
width: 100%;
height: 100%;
transform: rotate(270deg);
}
.media-360-viewer__loader--text {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
}
.media-360-viewer__loader--text p {
font-size: 100%;
font-weight: bold;
color: #fff;
}
.media-360-viewer__loader--text p.large {
font-size: 150%;
}
.media-360-viewer__loader--background {
stroke-dasharray: 0;
stroke-dashoffset: 0;
stroke: rgba(0, 0, 0, 0.7);
stroke-width: 25px;
}
.media-360-viewer__loader--cover {
stroke-dasharray: 200%;
stroke: #848484;
stroke-width: 15px;
stroke-linecap: round;
}
.media-360-viewer__loader--background,
.media-360-viewer__loader--cover {
fill: transparent;
}
.media-360-viewer__viewport {
position: absolute;
top: 10px;
left: 10px;
z-index: 2;
overflow: hidden;
}
.media-360-viewer__viewport--image {
width: 100%;
pointer-events: none;
}
.media-360-viewer__viewport--zoom {
position: absolute;
bottom: 5px;
right: 5px;
color: #fff;
z-index: 3;
font-size: 12px;
pointer-events: none;
}
.media-360-viewer__viewport--square {
display: block;
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
box-shadow: rgba(0, 0, 0, 0.8) 0 0 0 10000px;
cursor: grab;
transition: background ease-in-out 0.1s;
}
.media-360-viewer__viewport--square:hover {
background: rgba(255, 255, 255, 0.2);
}
.media-360-viewer__tools {
position: absolute;
bottom: 0;
left: 0;
width: 100%;
display: flex;
align-items: center;
justify-content: center;
padding-bottom: 10px;
}
.media-360-viewer__tools > a {
margin: 0 5px;
color: #000;
background: #fff;
border-radius: 50%;
width: 40px;
text-align: center;
line-height: 40px;
}
.media-360-viewer__tools > a[disabled] {
opacity: .5;
cursor: not-allowed;
}
.media-360-viewer__tools > a[disabled]:hover {
color: #000;
background: #fff;
}
.media-360-viewer__tools > a:hover {
background: #000;
color: #fff;
}
.media-360-viewer__tools--autoplay:before {
font-family: 'ClickIcons';
content: '\ea81';
}
.media-360-viewer__tools--autoplay.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></test>
</div>
<script type="text/x-template" id="template">
<div class="media-360-viewer" ref="container">
<transition name="fade">
<div class="media-360-viewer__viewport" v-if="zoomLevel > 1 && viewportEnabled" :style="{ width: (viewportScale * 100) + '%' }">
<img tabindex="1" draggable="false" alt="Viewport" class="media-360-viewer__viewport--image" src="https://www.bennetts.co.uk/-/media/bikesocial/2019-september-images/2020-yamaha-yzf-r1-and-r1m-review/2020-yamaha-r1-and-r1m_005.ashx?h=493&w=740&la=en&hash=F97CD240F0DDFA9540E912DCF7F07019017035C6">
<span class="media-360-viewer__viewport--zoom">
x{{ Math.round(zoomLevel * 10) / 10 }}
</span>
<span :style="{ transform: viewportTransform }" @mouseup="handleEnd" @mousedown="handleStart" @mousemove="handleMove($event, true)" @touchstart="handleStart" @touchend="handleEnd" @touchmove="handleMove($event, true)" class="media-360-viewer__viewport--square"></span>
</div>
</transition>
<img tabindex="1" ref="image" draggable="false" src="https://www.bennetts.co.uk/-/media/bikesocial/2019-september-images/2020-yamaha-yzf-r1-and-r1m-review/2020-yamaha-r1-and-r1m_005.ashx?h=493&w=740&la=en&hash=F97CD240F0DDFA9540E912DCF7F07019017035C6"
:style="{ transform: transform }" :class="{
canTranslate: zoomLevel > 1 && zoomEnabled,
canRotate: zoomLevel === 1,
isTranslating: zoomLevel > 1 && zoomEnabled && isMoving
}" @mouseup="handleEnd" @mousedown="handleStart" @mousemove="handleMove" @touchstart="handleStart" @touchend="handleEnd" @touchmove="handleMove" @dblclick="zoom" @wheel="zoomWheel" alt="360 Image" />
</div>
</script>
A part of this problem is a result of calling Math.floor
all the time. Each time you call Math.floor
your next calculations will be less accurate.
If you still want to round numbers, do it only at the end of your calculations chain or even in the place where you are using the variable. For example:
transform: function() {
const translateX = Math.floor(this.translateX)
const translateY = Math.floor(this.translateY)
return `scale(${this.zoomLevel}) translateX(${translateX}px) translateY(${translateY}px)`;
}
However when moving the image around it is moving faster than the mouse
This can be fixed by dividing number you're adding to the translate value by scale factor, which is determined by zoomLevel
like so:
if (translate.left >= 0 && translate.right <= 0) {
this.translateX += move.x / this.zoomLevel
}
if (translate.top >= 0 && translate.bottom <= 0) {
this.translateY += move.y / this.zoomLevel
}
Viewport is still somehow broken too because of scaling, we need to adjust translation value too, but this time not using zoomLevel
but scale of the viewport which is stored in this.viewportScale
. So, merging two of solutions together we now have code like this:
if (translate.left >= 0 && translate.right <= 0) {
if (viewport) {
this.translateX += move.x / this.viewportScale
} else {
this.translateX += move.x / this.zoomLevel
}
}
if (translate.top >= 0 && translate.bottom <= 0) {
if (viewport) {
this.translateY += move.y / this.viewportScale
} else {
this.translateY += move.y / this.zoomLevel
}
}
I also feel like when i'm clicking and dragging on the viewport I shouldn't be reversing the values twice
I don't think that there is a better way to do it unless you want to make two inner translate functions that will handle translation depending on the input source (viewport or image). It will be definetely cleaner approach to coding. For example if in future you would like to write some viewport specific code you will not need to include another and another if
. Your function could like like so:
translate (positions, element, zooming, viewport) {
positions = positions || {
x: this.startX,
y: this.startY
}
if (viewport) {
this._translateFromViewport(positions, element, zooming)
} else {
this._translateFromImage(positions, element, zooming)
}
this.startX = positions.x
this.startY = positions.y
}
where _translateFromViewport
and _translateFromImage
functions hold feature specific code.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With