vue-photo-zoom-pro
Version:
Vue picture magnifier
816 lines (776 loc) • 19.8 kB
JavaScript
import { openBlock, createElementBlock, normalizeStyle, createElementVNode, renderSlot, createCommentVNode, normalizeClass, mergeProps, resolveComponent, withDirectives, createBlock, vShow, withCtx } from 'vue';
var script$7 = {
name: 'VuePhotoZoomProMask',
props: {
width: {
type: Number,
default: 0
},
height: {
type: Number,
default: 0
},
selector: {
type: Object,
default: () => ({ left: 0, top: 0, width: 0, height: 0 })
},
maskColor: {
type: String,
default: ''
}
},
computed: {
topStyle () {
const { selector } = this;
return {
width: `${this.width}px`,
height: `${selector.top}px`
}
},
leftStyle () {
const { selector } = this;
return {
width: `${selector.left}px`,
height: `${selector.height}px`
}
},
rightStyle () {
const { selector } = this;
return {
width: `${this.width - selector.left - selector.width}px`,
height: `${selector.height}px`
}
},
bottomStyle () {
const { selector } = this;
return {
width: `${this.width}px`,
height: `${this.height - selector.top - selector.height}px`
}
},
maskStyle () {
return {
backgroundColor: this.maskColor
}
}
}
};
function render$7(_ctx, _cache, $props, $setup, $data, $options) {
return (openBlock(), createElementBlock("div", {
class: "mask",
style: normalizeStyle($options.maskStyle)
}, [
createElementVNode("div", {
class: "block top",
style: normalizeStyle($options.topStyle)
}, null, 4),
createElementVNode("div", {
class: "block left",
style: normalizeStyle($options.leftStyle)
}, null, 4),
createElementVNode("div", {
class: "block right",
style: normalizeStyle($options.rightStyle)
}, null, 4),
createElementVNode("div", {
class: "block bottom",
style: normalizeStyle($options.bottomStyle)
}, null, 4)
], 4))
}
script$7.render = render$7;
script$7.__scopeId = "data-v-15e50313";
var script$6 = {
name: 'VuePhotoZoomProZoomer',
props: {
left: {
type: Number,
default: 0
},
top: {
type: Number,
default: 0
},
scale: {
type: Number,
default: 2
},
zoomRegion: {
type: Object,
default: () => ({ width: 0, height: 0 })
},
width: {
type: Number,
default: 0
},
height: {
type: Number,
default: 0
}
},
computed: {
zoomerStyle () {
return {
width: `${this.width}px`,
height: `${this.height}px`
}
},
positionStyle () {
return {
transform: `translate3d(${-this.left}px, ${-this.top}px, 0)`
}
},
zoomedStyle () {
const { zoomRegion } = this;
return {
transform: `scale(${this.scale}) translateZ(0)`,
width: `${zoomRegion.width}px`,
height: `${zoomRegion.height}px`
}
}
}
};
function render$6(_ctx, _cache, $props, $setup, $data, $options) {
return (openBlock(), createElementBlock("div", {
class: "zoomer",
style: normalizeStyle($options.zoomerStyle)
}, [
createElementVNode("div", {
class: "position",
style: normalizeStyle($options.positionStyle)
}, [
(_ctx.$slots.default)
? (openBlock(), createElementBlock("div", {
key: 0,
class: "custom-zoomer",
style: normalizeStyle($options.zoomedStyle)
}, [
renderSlot(_ctx.$slots, "default")
], 4))
: createCommentVNode("", true)
], 4)
], 4))
}
script$6.render = render$6;
script$6.__scopeId = "data-v-1bae271a";
var script$5 = {
name: 'VuePhotoZommProSelector',
props: {
left: {
type: Number,
default: 0
},
top: {
type: Number,
default: 0
},
width: {
type: Number,
default: 0
},
height: {
type: Number,
default: 0
},
type: {
type: String,
default: 'square'
}
},
computed: {
selectorStyle () {
return {
width: `${this.width}px`,
height: `${this.height}px`,
transform: `translate3d(${this.left}px, ${this.top}px, 0)`
}
}
}
};
function render$5(_ctx, _cache, $props, $setup, $data, $options) {
return (openBlock(), createElementBlock("div", {
class: normalizeClass(['selector', $props.type]),
style: normalizeStyle($options.selectorStyle)
}, [
renderSlot(_ctx.$slots, "default")
], 6))
}
script$5.render = render$5;
script$5.__scopeId = "data-v-3c631a3a";
var script$4 = {
name: 'ImgPreview',
props: {
url: {
type: String,
default: ''
}
}
};
const _hoisted_1$1 = ["src"];
function render$4(_ctx, _cache, $props, $setup, $data, $options) {
return (openBlock(), createElementBlock("img", mergeProps({
class: "img-preview",
style: {"display":"block","width":"100%"},
src: $props.url
}, _ctx.$attrs), null, 16, _hoisted_1$1))
}
script$4.render = render$4;
var script$3 = {
name: 'ImgZoomer',
props: {
url: {
type: String,
default: ''
}
}
};
const _hoisted_1 = ["src"];
function render$3(_ctx, _cache, $props, $setup, $data, $options) {
return (openBlock(), createElementBlock("img", mergeProps({
class: "img-zoomer",
style: {"display":"block","width":"100%","height":"100%"},
src: $props.url
}, _ctx.$attrs), null, 16, _hoisted_1))
}
script$3.render = render$3;
const getBoundingClientRect = (element) => {
const rect = element.getBoundingClientRect();
// whether the IE version is lower than 11
const isIE = navigator.userAgent.indexOf('MSIE') !== -1;
// fix ie document bounding top always 0 bug
const rectTop =
isIE && element.tagName === 'HTML' ? -element.scrollTop : rect.top;
return {
left: rect.left,
top: rectTop,
right: rect.right,
bottom: rect.bottom,
width: rect.right - rect.left,
height: rect.bottom - rectTop
}
};
function clamp (num, min, max) {
const MIN = min;
const MAX = max;
return Math.min(Math.max(num, MIN), MAX)
}
const getBoundValue = (value, bound) => {
return {
x: clamp(value.x, bound.left, bound.right),
y: clamp(value.y, bound.top, bound.bottom)
}
};
const generateBound = (width, height, boundWidth, boundHeight) => {
return {
left: boundWidth,
top: boundHeight,
right: width - boundWidth,
bottom: height - boundHeight
}
};
const getScrollInfo = () => {
const { documentElement, body } = document;
const scrollTop =
documentElement.scrollTop || window.pageYOffset || body.scrollTop;
const scrollLeft =
documentElement.scrollLeft || window.pageXOffset || body.scrollLeft;
return {
scrollTop,
scrollLeft
}
};
/**
* load img
* @param {String} img url
* @return {Promise}
*/
const loadImg = (url, callback) => {
const img = document.createElement('img');
img.addEventListener('load', () => callback(null, img));
img.addEventListener('error', callback);
img.src = url;
};
function rotateCanvas (canvas, img, width, height, step) {
const ctx = canvas.getContext('2d');
const degree = (step * 90 * Math.PI) / 180;
const { width: imgWidth, height: imgHeight } = img;
switch (step) {
case 0:
canvas.width = imgWidth;
canvas.height = imgHeight;
ctx.drawImage(img, 0, 0, imgWidth, imgHeight);
canvas.style.width = width + 'px';
canvas.style.height = height + 'px';
break
case 1:
canvas.width = imgHeight;
canvas.height = imgWidth;
ctx.rotate(degree);
ctx.drawImage(img, 0, -imgHeight, imgWidth, imgHeight);
canvas.style.width = height + 'px';
canvas.style.height = width + 'px';
break
case 2:
canvas.width = imgWidth;
canvas.height = imgHeight;
ctx.rotate(degree);
ctx.drawImage(img, 0, 0, -imgWidth, -imgHeight);
canvas.style.width = width + 'px';
canvas.style.height = height + 'px';
break
case 3:
canvas.width = imgHeight;
canvas.height = imgWidth;
ctx.rotate(degree);
ctx.drawImage(img, -imgWidth, 0, imgWidth, imgHeight);
canvas.style.width = height + 'px';
canvas.style.height = width + 'px';
break
}
}
var script$2 = {
name: 'CanvasPreview',
props: {
url: {
type: String,
default: ''
},
rotate: {
type: Number,
default: 0
},
width: {
type: Number,
default: 0
},
height: {
type: Number,
default: 0
}
},
computed: {
step () {
return parseInt((this.rotate % 360) / 90, 10)
}
},
watch: {
step (newValue) {
this.handleRotate(newValue);
},
url: {
handler (url) {
loadImg(url, (err, img) => {
if (err) return console.error(err)
this.$img = img;
this.handleRotate(this.step);
});
},
immediate: true
}
},
mounted () {
this.$canvas = this.$refs.canvas;
},
methods: {
handleRotate (step) {
rotateCanvas(
this.$canvas,
this.$img,
this.width || this.$img.width,
this.height || this.$img.height,
step
);
}
}
};
function render$2(_ctx, _cache, $props, $setup, $data, $options) {
return (openBlock(), createElementBlock("canvas", mergeProps({
ref: "canvas",
class: "canvas-preview"
}, _ctx.$attrs), null, 16))
}
script$2.render = render$2;
var script$1 = {
name: 'CanvasZoomer',
props: {
url: {
type: String,
default: ''
},
rotate: {
type: Number,
default: 0
},
width: {
type: Number,
default: 0
},
height: {
type: Number,
default: 0
}
},
computed: {
step () {
return parseInt((this.rotate % 360) / 90, 10)
}
},
watch: {
step (newValue) {
this.handleRotate(newValue);
},
url: {
handler (url) {
loadImg(url, (err, img) => {
if (err) return console.error(err)
this.$img = img;
this.handleRotate(this.step);
});
},
immediate: true
}
},
mounted () {
this.$canvas = this.$refs.canvas;
},
methods: {
handleRotate (step) {
rotateCanvas(
this.$canvas,
this.$img,
this.width || this.$img.width,
this.height || this.$img.height,
step
);
}
}
};
function render$1(_ctx, _cache, $props, $setup, $data, $options) {
return (openBlock(), createElementBlock("canvas", mergeProps({
ref: "canvas",
class: "canvas-zoomer"
}, _ctx.$attrs), null, 16))
}
script$1.render = render$1;
var script = {
name: 'VuePhotoZoomPro',
components: {
PhotoMask: script$7,
Selector: script$5,
Zoomer: script$6,
ImgPreview: script$4,
ImgZoomer: script$3
},
props: {
width: {
type: Number,
default: 168
},
height: {
type: Number,
default: -1
},
url: {
type: String,
default: ''
},
highUrl: {
type: String,
default: ''
},
type: {
type: String,
default: 'square',
validator: function (value) {
return ['circle', 'square'].indexOf(value) !== -1
}
},
scale: {
type: Number,
default: 2
},
selector: {
type: [Boolean, Object],
default: true
},
outZoomer: {
type: [Boolean, Object],
default: false
},
disabledReactive: {
type: Boolean,
default: false
},
disabledEvent: {
type: Boolean,
default: false
},
mask: {
type: Boolean,
default: false
},
maskColor: {
type: String,
default: ''
}
},
data () {
return {
mouseEnterFlag: false,
outZoomerTop: 0,
mouse: {
x: 0,
y: 0
},
zoomRegionRect: {
left: 0,
top: 0,
width: 0,
height: 0
}
}
},
computed: {
selectorOptions () {
return typeof this.selector === 'object'
? this.selector
: { release: false }
},
outZoomerOptions () {
return typeof this.outZoomer === 'object'
? this.outZoomer
: { sticky: false }
},
selectorWidth () {
return this.width
},
selectorHeight () {
return this.height > -1 ? this.height : this.width
},
selectorHalfWidth () {
return this.selectorWidth / 2
},
selectorHalfHeight () {
return this.selectorHeight / 2
},
zoomerWidth () {
return this.outZoomer
? this.selectorWidth * this.scale
: this.selectorWidth
},
zoomerHeight () {
return this.outZoomer
? this.selectorHeight * this.scale
: this.selectorHeight
},
zoomerHalfWidth () {
return this.zoomerWidth / 2
},
zoomerHalfHeight () {
return this.zoomerHeight / 2
},
zoomRegionAbsolute () {
const { zoomRegionRect } = this;
const { scrollTop, scrollLeft } = getScrollInfo();
return {
left: zoomRegionRect.left + scrollLeft,
top: zoomRegionRect.top + scrollTop
}
},
pointBound () {
const { zoomRegionRect } = this;
return generateBound(
zoomRegionRect.width,
zoomRegionRect.height,
this.selectorHalfWidth,
this.selectorHalfHeight
)
},
vPointBound () {
const { zoomRegionRect, scale } = this;
return generateBound(
zoomRegionRect.width * scale,
zoomRegionRect.height * scale,
this.zoomerHalfWidth,
this.zoomerHalfHeight
)
},
point () {
const { mouse, selectorOptions } = this;
return !selectorOptions.release
? getBoundValue(mouse, this.pointBound)
: mouse
},
vPoint () {
const { mouse, scale, selectorOptions } = this;
const scaleMouse = { x: mouse.x * scale, y: mouse.y * scale };
return !selectorOptions.release
? getBoundValue(scaleMouse, this.vPointBound)
: scaleMouse
},
selectorProps () {
const { point } = this;
return {
width: this.selectorWidth,
height: this.selectorHeight,
left: point.x - this.selectorHalfWidth,
top: point.y - this.selectorHalfHeight
}
},
zoomerProps () {
const { vPoint } = this;
return {
scale: this.scale,
zoomRegion: this.zoomRegionRect,
width: this.zoomerWidth,
height: this.zoomerHeight,
left: vPoint.x - this.zoomerHalfWidth,
top: vPoint.y - this.zoomerHalfHeight
}
},
outZoomerPosition () {
return {
top: `${this.outZoomerTop}px`
}
}
},
watch: {
scale () {
this.handleMouseMove(this.pointerInfo);
},
zoomRegionRect: {
handler () {
this.$emit('update', this.zoomRegionRect);
},
deep: true
}
},
mounted () {
this.$zoomRegion = this.$refs.zoomRegion;
this.update();
this.$emit('created');
},
methods: {
handleMouseEnter () {
this.mouseEnter();
},
handleMouseMove (e) {
e = this.pointerInfo = e || this.pointerInfo;
if (e) {
const { pageX, pageY } = e;
const { zoomRegionAbsolute } = this;
this.mouseMove(
pageX - zoomRegionAbsolute.left,
pageY - zoomRegionAbsolute.top
);
if (this.outZoomer && this.outZoomerOptions.sticky) {
this.outZoomerTop = Math.max(pageY - e.clientY, 0);
}
}
},
handleMouseLeave () {
this.mouseLeave();
},
mouseEnter () {
!this.disabledReactive && this.update();
this.mouseEnterFlag = true;
},
mouseMove (x, y) {
!this.disabledReactive && this.update();
const { mouse } = this;
mouse.x = x;
mouse.y = y;
},
mouseLeave () {
this.mouseEnterFlag = false;
},
update () {
Object.assign(
this.zoomRegionRect,
getBoundingClientRect(this.$zoomRegion)
);
}
}
};
function render(_ctx, _cache, $props, $setup, $data, $options) {
const _component_PhotoMask = resolveComponent("PhotoMask");
const _component_ImgZoomer = resolveComponent("ImgZoomer");
const _component_Zoomer = resolveComponent("Zoomer");
const _component_Selector = resolveComponent("Selector");
const _component_ImgPreview = resolveComponent("ImgPreview");
return (openBlock(), createElementBlock("div", {
ref: "zoomRegion",
class: "vue-photo-zoom-pro",
onMouseenter: _cache[0] || (_cache[0] = $event => (!$props.disabledEvent && $options.handleMouseEnter($event))),
onMousemove: _cache[1] || (_cache[1] = $event => (!$props.disabledEvent && $options.handleMouseMove($event))),
onMouseleave: _cache[2] || (_cache[2] = $event => (!$props.disabledEvent && $options.handleMouseLeave($event)))
}, [
($props.mask)
? withDirectives((openBlock(), createBlock(_component_PhotoMask, {
key: 0,
width: $data.zoomRegionRect.width,
height: $data.zoomRegionRect.height,
"mask-color": $props.maskColor,
selector: $options.selectorProps
}, null, 8, ["width", "height", "mask-color", "selector"])), [
[vShow, $data.mouseEnterFlag]
])
: createCommentVNode("", true),
($props.selector)
? withDirectives((openBlock(), createBlock(_component_Selector, mergeProps({ key: 1 }, $options.selectorProps, { type: $props.type }), {
default: withCtx(() => [
(!$props.outZoomer)
? (openBlock(), createBlock(_component_Zoomer, mergeProps({
key: 0,
class: "inner-zoomer"
}, $options.zoomerProps), {
default: withCtx(() => [
($props.highUrl || $props.url)
? (openBlock(), createBlock(_component_ImgZoomer, {
key: 0,
url: $props.highUrl || $props.url
}, null, 8, ["url"]))
: createCommentVNode("", true),
renderSlot(_ctx.$slots, "zoomer")
]),
_: 3
}, 16))
: createCommentVNode("", true),
renderSlot(_ctx.$slots, "selector")
]),
_: 3
}, 16, ["type"])), [
[vShow, $data.mouseEnterFlag]
])
: createCommentVNode("", true),
($props.outZoomer)
? withDirectives((openBlock(), createBlock(_component_Zoomer, mergeProps({
key: 2,
class: "out-zoomer"
}, $options.zoomerProps, { style: $options.outZoomerPosition }), {
default: withCtx(() => [
($props.highUrl || $props.url)
? (openBlock(), createBlock(_component_ImgZoomer, {
key: 0,
url: $props.highUrl || $props.url
}, null, 8, ["url"]))
: createCommentVNode("", true),
renderSlot(_ctx.$slots, "zoomer")
]),
_: 3
}, 16, ["style"])), [
[vShow, $data.mouseEnterFlag]
])
: createCommentVNode("", true),
($props.url)
? (openBlock(), createBlock(_component_ImgPreview, {
key: 3,
url: $props.url
}, null, 8, ["url"]))
: createCommentVNode("", true),
renderSlot(_ctx.$slots, "default")
], 544))
}
script.render = render;
script.__scopeId = "data-v-61213061";
export { script$2 as CanvasPreview, script$1 as CanvasZoomer, script$4 as ImgPreview, script$3 as ImgZoomer, script as default };