<template>
<div class="zoom-wrapper" v-bind:style="{ width: svgWidth + 'px', height: svgHeight + 'px' }">
<svg ref="svg" v-bind:width="svgWidth" v-bind:height="svgHeight">
<image xmlns:xlink="http://www.w3.org/1999/xlink"
x="0" y="0"
v-bind:width="svgWidth" v-bind:height="svgHeight"
v-bind:xlink:href="image"></image>
<rect ref="handler"
class="move-handler"
v-bind:x="x" v-bind:y="y"
v-bind:width="width" v-bind:height="height"
style="fill: black; opacity: 0.4"></rect>
</svg>
</div>
</template>
<script>
import Snap from 'snapsvg'
// See http://svg.dabbles.info/snaptut-drag-limit
Snap.plugin( function( Snap, Element, Paper, global ) {
Element.prototype.limitDrag = function( params ) {
var onMove = function(dx, dy) {
limitMoveDrag.apply(this, arguments);
params.onMove.apply(this, arguments);
}
this.data('minx', params.minx); this.data('miny', params.miny);
this.data('maxx', params.maxx); this.data('maxy', params.maxy);
this.data('x', params.x);
this.data('y', params.y);
this.data('ibb', this.getBBox());
this.data('ot', this.transform().local);
this.drag(onMove, limitStartDrag, params.onEnd);
return this;
};
function limitMoveDrag( dx, dy ) {
var tdx, tdy;
var sInvMatrix = this.transform().globalMatrix.invert();
sInvMatrix.e = sInvMatrix.f = 0;
tdx = sInvMatrix.x( dx,dy ); tdy = sInvMatrix.y( dx,dy );
this.data('x', +this.data('ox') + tdx);
this.data('y', +this.data('oy') + tdy);
if (this.data('x') > this.data('maxx')) {
this.data('x', this.data('maxx'))
};
if (this.data('y') > this.data('maxy')) {
this.data('y', this.data('maxy'))
};
if (this.data('x') < this.data('minx')) {
this.data('x', this.data('minx'))
};
if (this.data('y') < this.data('miny')) {
this.data('y', this.data('miny'))
};
this.transform( this.data('ot') + "t" + [ this.data('x'), this.data('y') ]);
};
function limitStartDrag( x, y, ev ) {
this.data('ox', this.data('x'));
this.data('oy', this.data('y'));
};
});
export default {
props: [
'image',
'viewport',
'parentViewBox',
'imageWidth',
'imageHeight',
],
data() {
return {
svgWidth: 0,
svgHeight: 0,
loaded: false,
viewBox: [0, 0, 0, 0],
ratioX: 1,
ratioY: 1,
}
},
computed: {
x: function() {
return this.parentViewBox[0] * this.ratioX
},
y: function() {
return this.parentViewBox[1] * this.ratioY
},
width: function() {
return this.parentViewBox[2] * this.ratioX
},
height: function() {
return this.parentViewBox[3] * this.ratioY
}
},
methods: {
reset: function() {
var handler = new Snap(this.$refs.handler);
handler.node.removeAttribute('transform');
},
getCenter: function() {
const bbox = new Snap(this.$refs.handler).getBBox();
return {
x: bbox.cx / this.ratioX,
y: bbox.cy / this.ratioY
}
}
},
mounted() {
const svg = new Snap(this.$refs.svg);
const handler = new Snap(this.$refs.handler);
var img = new Image();
img.onload = (e) => {
// The zoom needs to have the same dimensions as the viewport,
// in order to reflect the position accordingly
const imageSize = Math.max(img.width, img.height);
const viewportSize = Math.max(this.viewport.width, this.viewport.height);
const viewportRatio = imageSize / viewportSize;
const svgWidth = (this.viewport.width * viewportRatio);
const svgHeight = (this.viewport.height * viewportRatio);
const viewBox = [0, 0, svgWidth, svgHeight];
const ratioX = (viewBox[2] / this.parentViewBox[2])
const ratioY = (viewBox[3] / this.parentViewBox[3])
Object.assign(this, {
svgWidth: svgWidth,
svgHeight: svgHeight,
viewBox: viewBox,
ratioX: ratioX,
ratioY: ratioY,
loaded: true
});
svg.attr({
viewBox: viewBox
});
var self = this;
setTimeout(() => {
handler.limitDrag({
x: 0, y: 0,
minx: -(svgWidth / 2), miny: -(svgHeight / 2),
maxx: (svgWidth / 2), maxy: (svgHeight / 2),
onMove: function() {
self.$emit('move', {
x: this.getBBox().x / ratioX,
y: this.getBBox().y / ratioY
});
},
onEnd: function() {
this.node.removeAttribute('transform');
self.$emit('moveend')
}
});
}, 500);
}
img.src = this.image;
}
}
</script>
<style scoped>
.zoom-wrapper {
background-color: #fff;
}
.move-handler {
cursor: -moz-grab;
cursor: -webkit-grab;
cursor: grab;
}
</style>