@klesh/vue-crop
Version:
Light-weight Vue.js component for photo cropping with zooming/dragging support
426 lines (383 loc) • 14.4 kB
JavaScript
'use strict';Object.defineProperty(exports,'__esModule',{value:true});//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
var script = {
name: 'VueCrop',
props: {
src: {type: String},
value: {type: Object},
ratio: {type: Number}
},
computed: {
bgOverlapStyle: function bgOverlapStyle() {
var ref = this.bg;
var x = ref.x;
var y = ref.y;
var w = ref.w;
var h = ref.h;
return {
top: (y + "px"),
left: (x + "px"),
width: (w + "px"),
height: (h + "px"),
};
},
bgImageStyle: function bgImageStyle() {
return Object.assign({}, this.bgOverlapStyle,
{opacity: this.box.actived ? 0.3 : 1});
},
boxStyle: function boxStyle() {
var ref = this;
var box = ref.box;
var bg = ref.bg;
return {
top: ((box.y * bg.zr + bg.y - 1) + "px"),
left: ((box.x * bg.zr + bg.x - 1) + "px"),
width: ((box.w * bg.zr) + "px"),
height: ((box.h * bg.zr) + "px"),
};
},
boxImageStyle: function boxImageStyle() {
var ref = this;
var box = ref.box;
var bg = ref.bg;
return {
top: ((-box.y * bg.zr) + "px"),
left: ((-box.x * bg.zr) + "px"),
width: ((bg.w) + "px"),
height: ((bg.h) + "px"),
};
}
},
data: function data() {
return {
initials: [],
bg: { x: 0, y: 0, w: 0, h: 0, ow: null, oh: null, or: null, zr: 1 }, // background image
box: { x: 10, y: 10, w: 100, h: 100, ax: 0, ay: 0, actived: false, mode: null },
};
},
mounted: function mounted() {
var this$1 = this;
this.$watch('value', function () { return this$1.reload(); }, {deep: true});
this.$watch('src', function () { return this$1.reload(); });
setTimeout(function () { return this$1.reload(); });
},
methods: {
reload: function reload() {
var this$1 = this;
// get image original width/height
var i = new Image();
i.onload = function () {
var ref = this$1;
var bg = ref.bg;
if (bg.ow === i.width && bg.oh === i.height) { return; }
bg.ow = i.width;
bg.oh = i.height;
bg.or = bg.ow / bg.oh;
var r = this$1.$el.offsetWidth / this$1.$el.offsetHeight;
if (bg.or > r) {
bg.w = Math.min(bg.ow, this$1.$el.offsetWidth);
bg.h = bg.w / bg.or;
} else {
bg.h = Math.min(bg.oh, this$1.$el.offsetHeight);
bg.w = bg.h * bg.or;
}
bg.zr = bg.w / bg.ow;
bg.x = (this$1.$el.offsetWidth - bg.w) / 2;
bg.y = (this$1.$el.offsetHeight - bg.h) / 2;
};
i.src = this.src;
// load box
if (this.value) {
var ref = this.value;
var x = ref.x;
var y = ref.y;
var w = ref.w;
var h = ref.h;
this.box = Object.assign({}, this.box, {x: x, y: y, w: w, h: h, actived: true});
} else {
this.box.actived = false;
}
},
mouseWheel: function mouseWheel(e) {
var ref = this;
var bg = ref.bg;
// zoom image
var oldWidth = bg.w;
var w = bg.w + e.wheelDeltaY;
if (w < 50) { return; }
bg.w = w;
bg.h = bg.w / bg.or;
// move background with respect to cursor position
var r = bg.w / oldWidth - 1;
bg.x -= (e.offsetX) * r;
bg.y -= (e.offsetY) * r;
bg.zr = bg.w / bg.ow;
},
mouseDown: function mouseDown(e) {
this.initials[e.button] = {e: e, bg: Object.assign({}, this.bg), box: Object.assign({}, this.box)};
},
mouseUp: function mouseUp(e) {
this.initials[e.button] = null;
this.box.mode = null;
},
mouseMove: function mouseMove(e) {
var ref = this.initials;
var l = ref[0];
var r = ref[2];
var ref$1 = this;
var bg = ref$1.bg;
var box = ref$1.box;
if (r) { // moving canvas
bg.x = r.bg.x + e.x - r.e.x;
bg.y = r.bg.y + e.y - r.e.y;
}
if (l && e.target.tagName === 'IMG') { // creating / moving / resizing box
if (!box.actived && Math.abs(e.x - l.e.x) > 5 && Math.abs(e.y - l.e.y) > 5) {
var ax = l.e.offsetX / bg.zr;
var ay = l.e.offsetY / bg.zr;
if (ax > 0 && ay > 0) {
this.box = {ax: ax, ay: ay, mode: 'resizing', actived: true};
}
}
if (box.mode) {
var ref$2 = l.box;
var x = ref$2.x;
var y = ref$2.y;
var w = ref$2.w;
var h = ref$2.h;
if (box.mode === 'resizing') {
var ax$1 = box.ax;
var ay$1 = box.ay;
var mx, my;
if (~ax$1) {
mx = e.offsetX / bg.zr;
x = Math.min(mx, ax$1);
w = Math.abs(mx - ax$1);
}
if (~ay$1) {
my = e.offsetY / bg.zr;
y = Math.min(my, ay$1);
h = Math.abs(my - ay$1);
}
if (this.ratio) {
if (Math.abs(e.x - l.e.x) > Math.abs(e.y - l.e.y)) {
var nh = w / this.ratio;
if (my < ay$1) { y += h - nh; }
h = nh;
} else {
var nw = h * this.ratio;
if (mx < ax$1) { x += w - nw; }
w = nw;
}
}
if (x < 0 || x + w > bg.ow || y < 0 || y + h > bg.oh) { return; }
} else if (box.mode === 'moving') {
x = l.box.x + (e.x - l.e.x) / bg.zr;
y = l.box.y + (e.y - l.e.y) / bg.zr;
x = Math.min(Math.max(0, x), bg.ow - box.w);
y = Math.min(Math.max(0, y), bg.oh - box.h);
}
this.box = Object.assign({}, this.box, {x: x, y: y, w: w, h: h});
}
} },
setResizing: function setResizing(direction) {
var ref = this;
var box = ref.box;
box.ax = -1;
box.ay = -1;
if (~direction.indexOf('n')) { box.ay = box.y + box.h; }
if (~direction.indexOf('s')) { box.ay = box.y; }
if (~direction.indexOf('w')) { box.ax = box.x + box.w; }
if (~direction.indexOf('e')) { box.ax = box.x; }
box.mode = 'resizing';
},
setMoving: function setMoving() {
this.box.mode = 'moving';
},
ok: function ok() {
var ref = this.box;
var x = ref.x;
var y = ref.y;
var w = ref.w;
var h = ref.h;
this.$emit('input', Object.assign({}, this.value, {x: x, y: y, w: w, h: h}));
this.$emit('change');
}
}
};function normalizeComponent(template, style, script, scopeId, isFunctionalTemplate, moduleIdentifier
/* server only */
, shadowMode, createInjector, createInjectorSSR, createInjectorShadow) {
if (typeof shadowMode !== 'boolean') {
createInjectorSSR = createInjector;
createInjector = shadowMode;
shadowMode = false;
} // Vue.extend constructor export interop.
var options = typeof script === 'function' ? script.options : script; // render functions
if (template && template.render) {
options.render = template.render;
options.staticRenderFns = template.staticRenderFns;
options._compiled = true; // functional template
if (isFunctionalTemplate) {
options.functional = true;
}
} // scopedId
if (scopeId) {
options._scopeId = scopeId;
}
var hook;
if (moduleIdentifier) {
// server build
hook = function hook(context) {
// 2.3 injection
context = context || // cached call
this.$vnode && this.$vnode.ssrContext || // stateful
this.parent && this.parent.$vnode && this.parent.$vnode.ssrContext; // functional
// 2.2 with runInNewContext: true
if (!context && typeof __VUE_SSR_CONTEXT__ !== 'undefined') {
context = __VUE_SSR_CONTEXT__;
} // inject component styles
if (style) {
style.call(this, createInjectorSSR(context));
} // register component module identifier for async chunk inference
if (context && context._registeredComponents) {
context._registeredComponents.add(moduleIdentifier);
}
}; // used by ssr in case component is cached and beforeCreate
// never gets called
options._ssrRegister = hook;
} else if (style) {
hook = shadowMode ? function () {
style.call(this, createInjectorShadow(this.$root.$options.shadowRoot));
} : function (context) {
style.call(this, createInjector(context));
};
}
if (hook) {
if (options.functional) {
// register for functional component in vue file
var originalRender = options.render;
options.render = function renderWithStyleInjection(h, context) {
hook.call(context);
return originalRender(h, context);
};
} else {
// inject component registration as beforeCreate hook
var existing = options.beforeCreate;
options.beforeCreate = existing ? [].concat(existing, hook) : [hook];
}
}
return script;
}
var normalizeComponent_1 = normalizeComponent;function createInjectorSSR(context) {
if (!context && typeof __VUE_SSR_CONTEXT__ !== 'undefined') {
context = __VUE_SSR_CONTEXT__;
}
if (!context) { return function () {}; }
if (!('styles' in context)) {
context._styles = context._styles || {};
Object.defineProperty(context, 'styles', {
enumerable: true,
get: function get() {
return context._renderStyles(context._styles);
}
});
context._renderStyles = context._renderStyles || renderStyles;
}
return function (id, style) {
return addStyle(id, style, context);
};
}
function addStyle(id, css, context) {
var group = css.media || 'default' ;
var style = context._styles[group] || (context._styles[group] = {
ids: [],
css: ''
});
if (!style.ids.includes(id)) {
style.media = css.media;
style.ids.push(id);
var code = css.source;
style.css += code + '\n';
}
}
function renderStyles(styles) {
var css = '';
for (var key in styles) {
var style = styles[key];
css += '<style data-vue-ssr-id="' + Array.from(style.ids).join(' ') + '"' + (style.media ? ' media="' + style.media + '"' : '') + '>' + style.css + '</style>';
}
return css;
}
var server = createInjectorSSR;/* script */
var __vue_script__ = script;
/* template */
var __vue_render__ = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:"vp",on:{"mousewheel":function($event){$event.stopPropagation();$event.preventDefault();return _vm.mouseWheel($event)},"mousedown":_vm.mouseDown,"mouseup":_vm.mouseUp,"mousemove":_vm.mouseMove,"contextmenu":function($event){$event.preventDefault();},"dblclick":function($event){_vm.box.actived = false;}}},[_vm._ssrNode("<div class=\"movable bgoverlap\""+(_vm._ssrStyle(null,_vm.bgOverlapStyle, null))+"></div> <img"+(_vm._ssrAttr("src",_vm.src))+" draggable=\"false\" class=\"movable\""+(_vm._ssrStyle(null,_vm.bgImageStyle, null))+"> "+((_vm.box.actived)?("<div class=\"movable box\""+(_vm._ssrStyle(null,_vm.boxStyle, null))+"><div class=\"vp fill\"><img"+(_vm._ssrAttr("src",_vm.src))+" draggable=\"false\" class=\"movable\""+(_vm._ssrStyle(null,_vm.boxImageStyle, null))+"></div> <div class=\"indicator top left\"></div> <div class=\"indicator top center\"></div> <div class=\"indicator top right\"></div> <div class=\"indicator middle left \"></div> <div class=\"indicator middle right \"></div> <div class=\"indicator bottom left\"></div> <div class=\"indicator bottom center\"></div> <div class=\"indicator bottom right\"></div></div>"):"<!---->"))])};
var __vue_staticRenderFns__ = [];
/* style */
var __vue_inject_styles__ = function (inject) {
if (!inject) { return }
inject("data-v-03773cf4_0", { source: ".vp[data-v-03773cf4]{position:relative;overflow:hidden;user-select:none}.vp .movable[data-v-03773cf4]{position:absolute}.vp .bgoverlap[data-v-03773cf4]{background:#000}.vp .box[data-v-03773cf4]{border:solid 1px #00f}.vp .fill[data-v-03773cf4]{width:100%;height:100%}.vp .box .indicator[data-v-03773cf4]{position:absolute;background:#00f;border:solid 1px #fff;width:8px;height:8px;margin:-4px 0 0 -4px;box-sizing:border-box}.indicator.top[data-v-03773cf4]{top:0}.indicator.middle[data-v-03773cf4]{top:50%}.indicator.bottom[data-v-03773cf4]{top:100%}.indicator.left[data-v-03773cf4]{left:0}.indicator.center[data-v-03773cf4]{left:50%}.indicator.right[data-v-03773cf4]{left:100%}.indicator.top.left[data-v-03773cf4]{cursor:nw-resize}.indicator.top.center[data-v-03773cf4]{cursor:n-resize}.indicator.top.right[data-v-03773cf4]{cursor:ne-resize}.indicator.middle.left[data-v-03773cf4]{cursor:w-resize}.indicator.middle.right[data-v-03773cf4]{cursor:e-resize}.indicator.bottom.left[data-v-03773cf4]{cursor:sw-resize}.indicator.bottom.center[data-v-03773cf4]{cursor:s-resize}.indicator.bottom.right[data-v-03773cf4]{cursor:se-resize}", map: undefined, media: undefined });
};
/* scoped */
var __vue_scope_id__ = "data-v-03773cf4";
/* module identifier */
var __vue_module_identifier__ = "data-v-03773cf4";
/* functional template */
var __vue_is_functional_template__ = false;
var component = normalizeComponent_1(
{ render: __vue_render__, staticRenderFns: __vue_staticRenderFns__ },
__vue_inject_styles__,
__vue_script__,
__vue_scope_id__,
__vue_is_functional_template__,
__vue_module_identifier__,
undefined,
server
);// Import vue component
// install function executed by Vue.use()
function install(Vue) {
if (install.installed) { return; }
install.installed = true;
Vue.component('VueCrop', component);
}
// Create module definition for Vue.use()
var plugin = {
install: install,
};
// To auto-install when vue is found
/* global window global */
var GlobalVue = null;
if (typeof window !== 'undefined') {
GlobalVue = window.Vue;
} else if (typeof global !== 'undefined') {
GlobalVue = global.Vue;
}
if (GlobalVue) {
GlobalVue.use(plugin);
}
// Inject install function into component - allows component
// to be registered via Vue.use() as well as Vue.component()
component.install = install;
// It's possible to expose named exports when writing components that can
// also be used as directives, etc. - eg. import { RollupDemoDirective } from 'rollup-demo';
// export const RollupDemoDirective = component;
exports.default=component;