UNPKG

vue-instagram-cropper

Version:

Simple Image Cropper that works like Instagram for Vue

246 lines (212 loc) 9.56 kB
import u from '../core/util'; import events from '../core/events'; import debounce from '../lib/debounce'; import deepClone from '../lib/deepClone'; export default { methods: { /** * Set's image via the src-prop. * Image can be an URL or an object. * We need to debounce, so that the next image * is rendered correctly */ $_c_setImage: debounce(function setImage(inital = false) { if (typeof this.src === 'string') { this.$_c_setImageViaUrl(inital); return; } // Due to the validator of value, we can assume the properties are correct if (this.src && typeof this.src === 'object') { this.$_c_setImageViaObject(inital); return; } this.$_c_setPlaceholders(); this.$_c_reset_values(); }, 30), $_c_setImageViaUrl(initial) { let img = new Image(); let href = this.src; if (!/^data:/.test(href) && !/^blob:/.test(href)) { img.setAttribute('crossOrigin', 'anonymous'); } if (this.forceCacheBreak && href) { const src = new URL(href); src.searchParams.append('cors', Date.now()); href = src.href; } img.src = href; if (u.imageLoaded(img)) { this.$_c_onload(img, +img.dataset.exifOrientation, initial, true); } else { this.loading = true; this.$_c_paintBackground(); img.onload = () => { this.$_c_onload(img, +img.dataset.exifOrientation, initial, true); }; img.onerror = () => { this.emitEvent(events.IMAGE_ERROR_EVENT); img = new Image(); img.src = 'data:image/svg+xml,%3Csvg xmlns=\'http://www.w3.org/2000/svg\' viewBox=\'0 0 600 600\'%3E%3Cpath fill=\'%23fff\' d=\'M0 0H600V600H0z\'/%3E%3Cpath d=\'M231.17 366.43a15.88 15.88 0 0 1-15.88-15.88v-95.31a15.88 15.88 0 0 1 15.88-15.88h137.66a15.89 15.89 0 0 1 15.89 15.88v95.31a15.89 15.89 0 0 1-15.89 15.88zm2.65-90a18.53 18.53 0 1 0 18.53-18.53 18.52 18.52 0 0 0-18.53 18.52zm129.72 68.83v-37.07l-29-29a4 4 0 0 0-5.62 0l-44.84 44.84-18.33-18.33a4 4 0 0 0-5.62 0l-23.67 23.67v15.88z\' fill=\'%23252525\'/%3E%3Cpath d=\'M169.39 219.53a6.66 6.66 0 0 1-1.17-9.34l8.18-10.52a6.66 6.66 0 0 1 9.35-1.17l244.86 189.25a6.68 6.68 0 0 1 1.17 9.35l-8.18 10.51a6.66 6.66 0 0 1-9.35 1.17z\' fill=\'%23b1605f\'/%3E%3C/svg%3E'; img.onload = () => { this.$_c_onload(img, +img.dataset.exifOrientation, initial, true); }; }; } }, $_c_setImageViaObject(initial) { const src = deepClone(this.src); // can't be cloned const { img } = this.src; this.naturalHeight = img.naturalHeight; this.naturalWidth = img.naturalWidth; this.imageSet = true; this.skipScaleRatio = true; this.img = img; this.imgData = src.imgData; // Needed to trick the $Watcher with OldVal out. this.$nextTick(() => { this.imgData = src.imgData; this.scaleRatio = src.scaleRatio; this.skipScaleRatio = false; this.$_c_checkBounceness(); if (initial) { this.emitEvent(events.INITIAL_IMAGE_LOADED_EVENT); } }); this.scaleRatio = src.scaleRatio; }, $_c_onload(img, orientation = 1, initial, keepAspect = false) { if (this.imageSet) { this.remove(events.IMAGE_REMOVE_ONLOAD_EVENT); } this.img = img; if (orientation > 1) { if (!this.img) return; const tempImg = u.getRotatedImage(this.img, orientation); tempImg.onload = () => { this.img = tempImg; this.$_c_placeImage(keepAspect); }; } else { this.$_c_placeImage(keepAspect); } if (initial) { this.emitEvent(events.INITIAL_IMAGE_LOADED_EVENT); } }, $_c_placeImage(keepAspect) { if (!this.img) return; const { imgData } = this; this.naturalWidth = this.img.naturalWidth; this.naturalHeight = this.img.naturalHeight; imgData.startX = u.numberValid(imgData.startX) ? imgData.startX : 0; imgData.startY = u.numberValid(imgData.startY) ? imgData.startY : 0; if (this.preventWhiteSpace) { this.$_c_aspectFill(); } else if (!this.imageSet) { // This part is important, when you load your own images from your server. // Usually the aspectRatio is perfectly set and you don't want to change it. // For example if you are loading the image via URL src // and you don't want to change the aspect ratio if (keepAspect && !this.greaterThanMaximumAspectRatio && !this.smallerThanMinimumAspectRatio) { this.$_c_aspectFit(); } else { this.$_c_aspectFill(); } } else { // Called in $_c_onDimensionChange this.imgData.width = this.naturalWidth * this.scaleRatio; this.imgData.height = this.naturalHeight * this.scaleRatio; } this.move({ x: 0, y: 0 }); this.$_c_draw(); }, $_c_aspectFit() { const imgWidth = this.naturalWidth; const imgHeight = this.naturalHeight; let scaleRatio; this.skipScaleRatio = true; // image is wider than canvas if (this.aspectRatio > this.canvasRatio) { if (this.greaterThanMaximumAspectRatio) { this.imgData.height = this.outputWidth / this.maximumAspectRatio; this.imgData.width = (this.imgData.height / imgHeight) * imgWidth; this.$_c_bounceTopCenter(); this.$_c_bounceLeftCenter(); } else { scaleRatio = imgWidth / this.outputWidth; this.imgData.height = imgHeight / scaleRatio; this.imgData.width = this.outputWidth; this.$_c_bounceTopCenter(); this.$_c_bounceLeft(); } } else if (this.smallerThanMinimumAspectRatio) { this.imgData.width = this.outputHeight * this.minimumAspectRatio; this.imgData.height = (this.imgData.width / imgWidth) * imgHeight; this.$_c_bounceTopCenter(); this.$_c_bounceLeftCenter(); } else { scaleRatio = imgHeight / this.outputHeight; this.imgData.width = imgWidth / scaleRatio; this.imgData.height = this.outputHeight; this.$_c_bounceLeftCenter(); this.$_c_bounceTop(); } this.$nextTick(() => { this.skipScaleRatio = false; }); }, /** * Is like "cover". Fills ups the whole canvas */ $_c_aspectFill() { const imgWidth = this.naturalWidth; const imgHeight = this.naturalHeight; let scaleRatio; this.skipScaleRatio = true; if (this.aspectRatio > this.canvasRatio) { scaleRatio = imgHeight / this.outputHeight; this.imgData.width = imgWidth / scaleRatio; this.imgData.height = this.outputHeight; this.$_c_bounceLeftCenter(); this.$_c_bounceTop(); } else { scaleRatio = imgWidth / this.outputWidth; this.imgData.height = imgHeight / scaleRatio; this.imgData.width = this.outputWidth; this.$_c_bounceTopCenter(); this.$_c_bounceLeft(); } this.$nextTick(() => { this.skipScaleRatio = false; }); }, /** * That means the image is smaller than the canvas */ $_c_imageIsFullyZoomedOut() { if (this.aspectRatio > this.canvasRatio) { return this.imgData.width < this.outputWidth || this.greaterThanMaximumAspectCanvasRatio; } return this.imgData.height < this.outputHeight || this.smallerThanMinimumAspectCanvasRatio; }, /** * That means the image that the image is much more zoomed in. * Like fully covered or even more zoomed in. */ $_c_imageIsFullyZoomedIn() { if (this.aspectRatio > this.canvasRatio) { return this.imgData.height >= this.outputHeight; } return this.imgData.width >= this.outputWidth; }, $_c_imageReachedMaximumScale() { return this.scaleRatio >= this.maximumScaleRatio; }, $_c_updateVModel: debounce(function updateVModel() { this.$emit('update', this.getMetadata()); }, 20), }, };