Limit zoom handler to bounds.
<template>
<div>
<svg ref="svg" v-bind:width="thumbnailWidth" v-bind:height="thumbnailHeight">
<image xmlns:xlink="http://www.w3.org/1999/xlink"
x="0" y="0"
v-bind:width="thumbnailWidth" v-bind:height="thumbnailHeight"
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);
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('ibb').width) {
this.data('x', this.data('maxx') - this.data('ibb').width)
};
if (this.data('y') > this.data('maxy') - this.data('ibb').height) {
this.data('y', this.data('maxy') - this.data('ibb').height)
};
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',
'viewBox',
'imageWidth',
'imageHeight',
],
data() {
return {
thumbnailWidth: 0,
thumbnailHeight: 0,
loaded: false
}
},
computed: {
x: function() {
return this.viewBox[0] * this.getRatioX();
},
y: function() {
return this.viewBox[1] * this.getRatioY();
},
width: function() {
return this.viewBox[2] * this.getRatioX();
},
height: function() {
return this.viewBox[3] * this.getRatioY();
}
},
methods: {
reset: function() {
var handler = new Snap(this.$refs.handler);
handler.node.removeAttribute('transform');
},
getRatioX: function() {
if (this.imageWidth === 0) { return 0; }
return this.thumbnailWidth / this.imageWidth;
},
getRatioY: function() {
if (this.imageHeight === 0) { return 0; }
return this.thumbnailHeight / this.imageHeight;
}
},
mounted() {
var svg = new Snap(this.$refs.svg);
var handler = new Snap(this.$refs.handler);
var img = new Image();
img.onload = (e) => {
svg.attr({
viewBox: [0, 0, img.width, img.height]
});
Object.assign(this, {
thumbnailWidth: img.width,
thumbnailHeight: img.height,
loaded: true
});
var self = this;
handler.limitDrag({
x: 0, y: 0,
minx: -(img.width / 2), miny: -(img.height / 2),
maxx: (img.width / 2), maxy: (img.height / 2),
onMove: function() {
self.$emit('change', {
x: this.getBBox().x * (self.imageWidth / self.thumbnailWidth),
y: this.getBBox().y * (self.imageHeight / self.thumbnailHeight),
});
}
});
}
img.src = this.image;
}
}
</script>
<style scoped>
.move-handler {
cursor: -moz-grab;
cursor: -webkit-grab;
cursor: grab;
}
</style>