UNPKG

clipic

Version:

移动端图片裁剪工具

337 lines (316 loc) 13.7 kB
(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : typeof define === 'function' && define.amd ? define(factory) : (global = global || self, global.Clipic = factory()); }(this, function () { 'use strict'; function styleInject(css, ref) { if ( ref === void 0 ) ref = {}; var insertAt = ref.insertAt; if (!css || typeof document === 'undefined') { return; } var head = document.head || document.getElementsByTagName('head')[0]; var style = document.createElement('style'); style.type = 'text/css'; if (insertAt === 'top') { if (head.firstChild) { head.insertBefore(style, head.firstChild); } else { head.appendChild(style); } } else { head.appendChild(style); } if (style.styleSheet) { style.styleSheet.cssText = css; } else { style.appendChild(document.createTextNode(css)); } } var css = ".clipic-body{background:#1c1c1c;position:fixed;width:100%;height:100%;top:0;left:0;-webkit-transform:translateY(100%);-ms-transform:translateY(100%);transform:translateY(100%);-webkit-transition:.4s;-o-transition:.4s;transition:.4s;-webkit-touch-callout:none;-webkit-user-select:none;z-index:99;overflow:hidden}.clipic-body,.clipic-body *{-webkit-box-sizing:border-box;box-sizing:border-box}.clipic-operation-bar{display:-webkit-box;display:-ms-flexbox;display:flex;color:#f2f2f2;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between;position:absolute;width:100%;bottom:0;left:0}.clipic-operation-bar [role=button]{padding:15px 20px;font-size:1em}.clipic-frame{background:#f2f2f2;position:absolute;left:50%;top:30px;transform:translateX(-50%);transition:.3s}.clipic-frame img{-webkit-touch-callout:none;pointer-events:none}.clipic-frame-show{overflow:hidden}.clipic-cancel{color:#e04c4c}.clipic-reset{color:#3680fd}.clipic-confirm{color:#23c667}.clipic-layer{position:fixed;width:100%;height:100%;top:0;left:0;background:rgba(0,0,0,.8);pointer-events:none;transform:translateZ(0)}"; styleInject(css); var dom = "\n <div class=\"clipic-frame\" id=\"clipicFrame1\"><img id=\"clipicImg1\"></div>\n <div class=\"clipic-layer\"></div>\n <div class=\"clipic-frame clipic-frame-show\" id=\"clipicFrame2\"><img id=\"clipicImg2\"></div>\n <div class=\"clipic-operation-bar\">\n <div class=\"clipic-cancel\" id=\"clipicCancel\" role=\"button\">\u53D6\u6D88</div>\n <div class=\"clipic-reset\" id=\"clipicReset\" role=\"button\">\u91CD\u7F6E</div>\n <div class=\"clipic-confirm\" id=\"clipicConfirm\" role=\"button\">\u5B8C\u6210</div>\n </div>\n "; var classCallCheck = function (instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }; var createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); var Clipic = function () { function Clipic() { classCallCheck(this, Clipic); this.default = { width: 500, // 裁剪宽度 height: 500, // 裁剪高度 src: '', // 需要裁剪的图片 encode: 'base64', // 导出格式,支持 base64|blob|file type: 'jpeg', // 裁剪后图片的类型,仅支持jpeg/png两种 name: 'clipic', // 如果导出格式位file, 则可以填写图片名 quality: 0.9, // 压缩质量 buttonText: ['取消', '重置', '完成'] // 底部三个按钮文案 }; this.init(); // 初始化,渲染dom跟css this.clipic = this.getId('clipic'); this.img1 = this.getId('clipicImg1'); // 背景图 this.img2 = this.getId('clipicImg2'); // 前景图 this.frame1 = this.getId('clipicFrame1'); // 背景操作框 this.frame2 = this.getId('clipicFrame2'); // 前景操作框 this.cancelBtn = this.getId('clipicCancel'); // 取消按钮 this.resetBtn = this.getId('clipicReset'); // 重置按钮 this.confirmBtn = this.getId('clipicConfirm'); // 完成按钮 this.reset = this.reset.bind(this); this.done = this.done.bind(this); this.cancel = this.cancel.bind(this); } createClass(Clipic, [{ key: 'init', value: function init() { if (!this.getId('clipic')) { this.createHtml(); } } }, { key: 'getId', value: function getId(id) { return document.getElementById(id); } }, { key: 'createHtml', value: function createHtml() { var div = document.createElement('div'); div.className = 'clipic-body'; div.setAttribute('id', 'clipic'); div.innerHTML = dom; document.body.appendChild(div); } }, { key: 'getImage', value: function getImage() { var _this = this; var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; // 初始化参数 this.scale = 1; // 缩放 this.rotate = 0; // 旋转 this.translateX = 0; // 水平偏移 this.translateY = 0; // 垂直偏移 var defaults = JSON.parse(JSON.stringify(this.default)); this.options = Object.assign(defaults, options); this.cancelBtn.innerHTML = this.options.buttonText[0]; this.resetBtn.innerHTML = this.options.buttonText[1]; this.confirmBtn.innerHTML = this.options.buttonText[2]; this.img1.src = this.options.src; this.img2.src = this.options.src; var tempImage = new Image(); tempImage.onload = function () { _this.originW = _this.img2.width; _this.originH = _this.img2.height; if (_this.options.ratio) { _this.options.width = _this.img2.width; _this.options.height = _this.img2.width / _this.options.ratio; } else { _this.options.ratio = _this.options.width / _this.options.height; } _this.originRatio = _this.originW / _this.originH; _this.initSize(); _this.clipic.style.transform = 'translate(0, 0)'; setTimeout(function () { if (_this.options.ratio > _this.originRatio) { _this.img1.style.width = _this.frame2.clientWidth + 'px'; _this.img2.style.width = _this.frame2.clientWidth + 'px'; } else { _this.img1.style.height = _this.frame2.clientHeight + 'px'; _this.img2.style.height = _this.frame2.clientHeight + 'px'; } }, 300); _this.setTransform(); _this.cancelBtn.addEventListener('click', _this.cancel); _this.resetBtn.addEventListener('click', _this.reset); _this.confirmBtn.addEventListener('click', _this.done); _this.clipic.addEventListener('touchmove', function (e) { e.preventDefault(); if (e.touches.length > 1) { _this.setScale(e.touches[0], e.touches[1]); _this.setRotate(e.touches[0], e.touches[1]); return; } _this.setTranslate(e.touches[0]); }); _this.clipic.addEventListener('touchend', function (e) { _this.distance = null; _this.angle = null; _this.moveX = null; _this.moveY = null; }); }; tempImage.src = this.options.src; } }, { key: 'initSize', value: function initSize() { var body = document.documentElement || document.body; var cw = body.clientWidth - 60; var ch = body.clientHeight - 80; this.frame1.style.width = cw + 'px'; this.frame1.style.height = cw / this.options.ratio + 'px'; this.frame2.style.width = cw + 'px'; this.frame2.style.height = cw / this.options.ratio + 'px'; if (cw / this.options.ratio > ch) { this.frame1.style.height = ch + 'px'; this.frame1.style.width = ch * this.options.ratio + 'px'; this.frame2.style.height = ch + 'px'; this.frame2.style.width = ch * this.options.ratio + 'px'; } } }, { key: 'setScale', value: function setScale(touches1, touches2) { var x = Math.abs(touches1.clientX - touches2.clientX); var y = Math.abs(touches1.clientY - touches2.clientY); var s = Math.sqrt(x * x + y * y); if (this.distance) { this.scale += (s - this.distance) / this.img2.clientWidth; this.setTransform(); } this.distance = s; } }, { key: 'setRotate', value: function setRotate(touches1, touches2) { var x = touches1.clientX - touches2.clientX; var y = touches1.clientY - touches2.clientY; var angle = Math.atan2(y, x) * 180 / Math.PI; if (this.angle) { this.rotate += angle - this.angle; this.setTransform(); } this.angle = angle; } }, { key: 'setTranslate', value: function setTranslate(touches) { var x = touches.clientX; var y = touches.clientY; if (this.moveX) { this.translateX += x - this.moveX; } if (this.moveY) { this.translateY += y - this.moveY; } this.moveX = x; this.moveY = y; this.setTransform(); } }, { key: 'setTransform', value: function setTransform() { var transform = 'translate(' + this.translateX + 'px, ' + this.translateY + 'px) scale(' + this.scale + ') rotate(' + this.rotate + 'deg)'; this.img1.style.transform = transform; this.img2.style.transform = transform; } }, { key: 'cancel', value: function cancel(eventType) { var _this2 = this; this.clipic.style.transform = 'translate(0, 100%)'; setTimeout(function () { _this2.img1.style = ''; _this2.img1.src = ''; _this2.img2.style = ''; _this2.img2.src = ''; }, 400); if (this.options.onCancel && eventType !== 'done') { this.options.onCancel(); } this.cancelBtn.removeEventListener('click', this.cancel); this.resetBtn.removeEventListener('click', this.reset); this.confirmBtn.removeEventListener('click', this.done, true); } }, { key: 'reset', value: function reset() { var _this3 = this; this.scale = 1; this.rotate = 0; this.translateX = 0; this.translateY = 0; this.img1.style.transition = '0.3s'; this.img2.style.transition = '0.3s'; this.setTransform(); setTimeout(function () { _this3.img1.style.transition = ''; _this3.img2.style.transition = ''; }, 300); } }, { key: 'done', value: function done() { var _this4 = this; var zommRatio = this.options.width / this.frame2.clientWidth; var canvas = document.createElement('canvas'); canvas.width = this.options.width; canvas.height = this.options.height; var ctx = canvas.getContext('2d'); ctx.fillStyle = '#fff'; ctx.fillRect(0, 0, canvas.width, canvas.height); var drawImageW = void 0; var drawImageH = void 0; if (this.options.ratio > this.originRatio) { drawImageW = this.options.width; drawImageH = this.originH / (this.originW / this.options.width); } else { drawImageH = this.options.height; drawImageW = this.originW / (this.originH / this.options.height); } var point = { x: drawImageW / 2, y: drawImageH / 2 }; ctx.translate(this.translateX * zommRatio, this.translateY * zommRatio); if (this.rotate !== 0) { ctx.translate(point.x, point.y); ctx.rotate(this.rotate * Math.PI / 180); ctx.translate(-point.x, -point.y); } if (this.scale !== 1) { ctx.translate(point.x * (1 - this.scale), point.y * (1 - this.scale)); ctx.scale(this.scale, this.scale); } ctx.drawImage(this.img2, 0, 0, drawImageW, drawImageH); if (this.options.onDone) { switch (this.options.encode) { case 'base64': this.options.onDone(canvas.toDataURL('image/' + this.options.type, this.options.quality)); break; case 'blob': canvas.toBlob(function (blob) { _this4.options.onDone(blob); }, 'image/' + this.options.type); break; case 'file': canvas.toBlob(function (blob) { var file = new window.File([blob], _this4.options.name, { type: 'image/' + _this4.options.type }); _this4.options.onDone(file); }, 'image/' + this.options.type); break; default: this.options.onDone(canvas.toDataURL('image/' + this.options.type, this.options.quality)); break; } } this.cancel('done'); } }]); return Clipic; }(); return Clipic; }));