├── .gitattributes
├── .gitignore
├── README.md
├── dist
├── crop.html
├── crop.min.js
└── crop.js
├── package.json
├── LICENSE
└── src
└── crop.js
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules
3 |
4 | # local env files
5 | .env.local
6 | .env.*.local
7 |
8 | # Log files
9 | npm-debug.log*
10 | yarn-debug.log*
11 | yarn-error.log*
12 |
13 | # Editor directories and files
14 | .idea
15 | .vscode
16 | *.suo
17 | *.ntvs*
18 | *.njsproj
19 | *.sln
20 | *.sw*
21 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 无依赖的图片裁剪库
2 | 
3 |
4 | ## 安装
5 |
6 | ```sh
7 | npm install --save @zee.kim/crop
8 | ```
9 |
10 | ## 使用
11 |
12 | ```javascript
13 | new crop({
14 | width: 300,
15 | height: 300,
16 | //url:"", //默认显示图片
17 | success: function (data) {
18 | console.log(data);//这是个base64 图片字符串
19 | }
20 | });
21 | ```
22 |
--------------------------------------------------------------------------------
/dist/crop.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | crop
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "private": false,
3 | "name": "@zee.kim/crop",
4 | "version": "1.0.0",
5 | "description": "图片裁剪",
6 | "main": "src/crop.js",
7 | "scripts": {
8 | "build": "node build.js"
9 | },
10 | "repository": {
11 | "type": "git",
12 | "url": "git+https://github.com/jinzhe/crop.git"
13 | },
14 | "keywords": [ "crop" ],
15 | "author": "zee kim",
16 | "license": "MIT",
17 | "bugs": {
18 | "url": "https://github.com/jinzhe/crop/issues"
19 | },
20 | "homepage": "https://zee.kim",
21 | "devDependencies": {
22 | "babel-core": "^6.22.1",
23 | "babel-preset-es2015-rollup": "^3.0.0",
24 | "rollup": "^0.41.4",
25 | "rollup-plugin-babel": "^2.7.1",
26 | "rollup-plugin-commonjs": "^7.0.0",
27 | "rollup-plugin-node-resolve": "^2.0.0",
28 | "rollup-plugin-uglify": "^1.0.1"
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 jinzhe
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/dist/crop.min.js:
--------------------------------------------------------------------------------
1 | var crop=function(){"use strict";var t=function(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")},e=function(){function t(t,e){for(var n=0;n0&&void 0!==arguments[0]?arguments[0]:{};t(this,n),this.width=i.width||400,this.height=i.height||400,this.quality=i.quality||.8,this.success=i.success,this.style();this.layout=document.createElement("DIV"),this.layout.classList.add("crop"),this.layout.classList.add("active"),this.layout.innerHTML='\n \n
\n
\n
\n \n \n
\n OK
\n ',document.querySelector("body").appendChild(this.layout),this.input=document.querySelector(i.input||".crop-file"),this.crop=document.querySelector(i.crop||".crop"),this.cropWall=document.querySelector(i.wall||".crop-wall"),this.cropImage=document.querySelector(i.image||".crop-image"),this.cropClose=document.querySelector(i.close||".crop-close"),this.cropOk=document.querySelector(i.ok||".crop-ok"),this.cropZoom=document.querySelector(i.zoom||".crop-zoom"),this.crop.style.width=this.width+"px",this.crop.style.height=this.height+"px",this.cropWall.style.width=this.width+"px",this.cropWall.style.height=this.height+"px",this.input.addEventListener("change",function(t){if(e.files=t.target.files||t.dataTransfer.files,0==e.files.length)return!1;e.getOrientation(e.files[0],function(t){e.orientation=t;var n=new FileReader;n.onload=function(t){e.open(e.dataURLtoBlob(t.target.result))},n.readAsDataURL(e.files[0])})}),void 0!=i.url&&this.open(i.url),this.cropClose.addEventListener("click",function(){e.layout.parentNode.removeChild(e.layout)}),this.canvas=document.createElement("canvas"),this.canvas.width=this.width,this.canvas.height=this.height,this.canvas.setAttribute("style","position:absolute;left:0;top:0;opacity:0;pointer-events:none;"),this.context=this.canvas.getContext("2d"),this.layout.appendChild(this.canvas)}return e(n,[{key:"style",value:function(){if(!document.querySelector("#cropStyle")){var t='\n .crop {\n position: absolute;\n left: 50%;\n top: 50%;\n transform: translate3d(-50%, -50%, 0);\n width: 400px;\n height: 400px;\n background: #000;\n box-shadow: 0 0 20px 2px rgba(0, 0, 0, .5);\n z-index: 20;\n transition: all .2s cubic-bezier(0.99, 0.01, 0.22, 0.94);\n }\n\n .crop.loading::after {\n content: "LOADING";\n position: absolute;\n left: 0;\n right: 0;\n top: 0;\n bottom: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 10px;\n background-color: rgba(0, 0, 0, .5);\n color: #666;\n }\n\n .crop-wall {\n position: relative;\n height: 100%;\n overflow: hidden;\n }\n .crop-wall img{\n user-select:none;\n }\n .crop-mask{\n pointer-events:none;\n position:absolute;\n left:0;\n right:0;\n top:0;\n bottom:0;\n }\n .crop-mask::before{\n content:"";\n position:absolute;\n left:0;\n right:0;\n top:0;\n height:120px;\n background-image: linear-gradient(0deg, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 0.8) 100%);\n }\n .crop-mask::after{\n content:"";\n position:absolute;\n left:0;\n right:0;\n bottom:0;\n height:120px;\n background-image: linear-gradient(0deg, rgba(0, 0, 0, 0.8) 0%, rgba(0, 0, 0, 0) 100%);\n }\n .crop-file {\n position: absolute;\n left: 15px;\n top: 15px;\n transition: .3s;\n cursor: pointer;\n }\n\n .crop-file:hover {\n transform: scale(1.2);\n }\n\n .crop-file svg {\n width: 22px;\n height: 22px;\n }\n\n .crop-file input {\n position: absolute;\n width: 100%;\n height: 100%;\n opacity: 0;\n cursor: pointer;\n }\n\n .crop.active .crop-file {\n position: absolute;\n left: 50%;\n top: 50%;\n transform: translate(-50%, -50%);\n transition: 0s;\n }\n\n .crop.loading .crop-file {\n opacity: 0;\n\n }\n\n .crop.active .crop-file svg {\n width: 50px;\n height: 50px;\n }\n\n .crop.active .crop-file svg path {\n fill: #333;\n }\n\n .crop-ok {\n position: absolute;\n right: 10px;\n bottom: 10px;\n width: 66px;\n height: 26px;\n border-radius: 40px;\n text-align: center;\n font-size: 10px;\n font-family: arial;\n line-height: 26px;\n border: 2px solid #fff;\n color: #fff;\n transition: all .2s ease-in-out;\n cursor: pointer;\n opacity: 0;\n }\n\n .crop-ok.show {\n opacity: 1;\n }\n\n .crop-ok:hover {\n background-color: rgba(255, 255, 255, 0.2);\n }\n\n .crop-close {\n position: absolute;\n top: 15px;\n right: 15px;\n width: 30px;\n height: 30px;\n cursor: pointer;\n transition: .3s;\n transform: scale(0.8);\n }\n\n .crop-close:hover {\n transform: scale(1);\n }\n\n .crop-close:before {\n position: absolute;\n content: \'\';\n width: 30px;\n height: 2px;\n background: white;\n transform: rotate(45deg);\n top: 14px;\n left: 0px;\n border-radius: 2px;\n }\n\n .crop-close:after {\n content: \'\';\n position: absolute;\n width: 30px;\n height: 2px;\n background: white;\n transform: rotate(-45deg);\n top: 14px;\n left: 0px;\n border-radius: 2px;\n }\n\n .crop-zoom {\n position: absolute;\n left: 10px;\n bottom: 20px;\n width: 100px;\n opacity: 0;\n transition: .3s;\n }\n\n .crop-zoom.show {\n opacity: 1;\n }\n\n input[type=range] {\n -webkit-appearance: none;\n\n }\n\n input[type=range]:focus {\n outline: none;\n }\n\n input[type=range]::-webkit-slider-runnable-track {\n width: 100%;\n height: 2px;\n border-radius: 2px;\n background: #fff;\n border: none;\n\n }\n\n input[type=range]::-webkit-slider-thumb {\n height: 16px;\n width: 16px;\n border-radius: 10px;\n background: #fff;\n cursor: pointer;\n -webkit-appearance: none;\n margin-top: -8px;\n transition: .3s;\n }\n\n input[type=range]::-webkit-slider-thumb:hover {\n transform: scale(1.2);\n }\n\n input[type=range]::-moz-range-track {\n width: 100%;\n height: 2px;\n background: #000;\n }\n\n input[type=range]::-moz-range-thumb {\n height: 16px;\n width: 16px;\n border-radius: 8px;\n border: 2px solid #efb708;\n background: #ffffff;\n cursor: pointer;\n }\n\n input[type=range]::-ms-track {\n width: 100%;\n height: 1px;\n cursor: pointer;\n background: transparent;\n border-color: transparent;\n color: transparent;\n }\n\n input[type=range]::-ms-fill-lower {\n background: rgba(0, 0, 0, 0.5);\n border: 0px solid rgba(200, 200, 200, 0.2);\n border-radius: 0px;\n box-shadow: 0px 0px 0px rgba(0, 0, 0, 0), 0px 0px 0px rgba(13, 13, 13, 0);\n }\n\n input[type=range]::-ms-fill-upper {\n background: rgba(0, 0, 0, 0.5);\n border: 0px solid rgba(200, 200, 200, 0.2);\n border-radius: 0px;\n box-shadow: 0px 0px 0px rgba(0, 0, 0, 0), 0px 0px 0px rgba(13, 13, 13, 0);\n }\n\n input[type=range]::-ms-thumb {\n height: 16px;\n width: 16px;\n border-radius: 8px;\n background: #ffffff;\n cursor: pointer;\n height: 1px;\n }\n\n input[type=range]:focus::-ms-fill-lower {\n background: rgba(0, 0, 0, 0.5);\n }\n\n input[type=range]:focus::-ms-fill-upper {\n background: rgba(0, 0, 0, 0.5);\n }\n ',e=document.createElement("style");e.type="text/css",e.id="cropStyle",e.styleSheet?e.styleSheet.cssText=t.replace(/\s/,""):e.innerHTML=t.replace(/\s/,""),document.getElementsByTagName("head")[0].appendChild(e)}}},{key:"open",value:function(t){var e=this,n=this;this.isMobile=/ios|android/.test(navigator.userAgent.toLowerCase()),this.beginX=0,this.beginY=0,this.scaleX=0,this.scaleY=0,this.scaleHeight=0,this.scaleWidth=0,this.isDrag=!1,this.crop.classList.add("loading"),this.img=new Image,this.img.crossOrigin="anonymous",this.img.onload=function(){if(e.crop.classList.remove("loading"),e.crop.classList.remove("active"),e.cropZoom.classList.add("show"),e.cropOk.classList.add("show"),e.temp={width:e.img.naturalWidth,height:e.img.naturalHeight},6==e.orientation&&(e.temp.width=e.img.naturalHeight,e.temp.height=e.img.naturalWidth),e.temp.width>e.temp.height?(e.scaleHeight=e.height,e.scaleWidth=Math.round(e.scaleHeight*e.temp.width/e.temp.height)):(e.scaleWidth=e.width,e.scaleHeight=Math.round(e.scaleWidth*e.temp.height/e.temp.width)),e.scaleX=-Math.round((e.scaleWidth-e.width)/2),e.scaleY=-Math.round((e.scaleHeight-e.height)/2),6==e.orientation?e.fixedOrientation(e.img,e.temp.width,e.temp.height,function(t){e.cropImage.setAttribute("src",t)}):e.cropImage.setAttribute("src",t),e.crop.style.width=e.width+"px",e.crop.style.height=e.height+"px",e.cropWall.style.width=e.width+"px",e.cropWall.style.height=e.height+"px",e.cropImage.style.position="absolute",e.cropImage.style.left=e.scaleX+"px",e.cropImage.style.top=e.scaleY+"px",e.cropImage.style.width=e.scaleWidth+"px",e.cropImage.style.height=e.scaleHeight+"px",e.cropImage.style.userSelect="none",e.cropZoom.value=100,e.cropCanvas(),!e.hasEvent){var i=function(){var t=e.canvas.toDataURL("image/jpeg",e.quality);e.success&&e.success(t.substr(23)),e.layout.parentNode.removeChild(e.layout)};e.cropZoom.addEventListener("input",function(t){n.zoom(t)}),e.cropOk.addEventListener("click",i),e.cropImage.addEventListener(n.isMobile?"touchstart":"mousedown",function(t){t.preventDefault(),n.isDrag=!0,n.beginX=t.pageX-t.target.offsetLeft,n.beginY=t.pageY-t.target.offsetTop;var e=function(t){if(!n.isDrag)return!1;n.isMobile?2==t.touches.length?(n.scaleLength=n.touchData(t).length,n.scale=Math.min(200,Math.max(100,n.scaleLength/n.beginLength*100)),this.zoom()):(n.scaleX=t.pageX-beginX,n.scaleY=t.pageY-beginY):(n.scaleX=t.pageX-n.beginX,n.scaleY=t.pageY-n.beginY),n.limit(),n.cropImage.style.width=n.scaleWidth+"px",n.cropImage.style.height=n.scaleHeight+"px",n.cropImage.style.left=n.scaleX+"px",n.cropImage.style.top=n.scaleY+"px"},i=function t(i){n.isDrag=!1,n.isMobile?(document.removeEventListener("touchmove",e,!1),document.removeEventListener("touchend",t,!1)):(document.removeEventListener("mousemove",e,!1),document.removeEventListener("mouseup",t,!1))};n.isMobile?(2==t.touches.length&&(n.beginLength=0==n.beginLength?n.touchData(t).length:n.beginLength),document.addEventListener("touchmove",e),document.addEventListener("touchend",i)):(document.addEventListener("mousemove",e),document.addEventListener("mouseup",i))}),e.hasEvent=!0}},this.img.src=t}},{key:"cropCanvas",value:function(){this.context.clearRect(0,0,this.width,this.height);var t=Math.round(Math.abs(this.scaleX)*this.img.naturalWidth/this.scaleWidth),e=Math.round(Math.abs(this.scaleY)*this.img.naturalHeight/this.scaleHeight),n=this.width*this.img.naturalWidth/this.scaleWidth>>0,i=this.height*this.img.naturalHeight/this.scaleHeight>>0;this.context.drawImage(this.img,t,e,n,i,0,0,this.width,this.height/this.cropRatio())}},{key:"cropRatio",value:function(){var t=(this.img.naturalWidth,this.img.naturalHeight),e=document.createElement("canvas");e.width=1,e.height=t;var n=e.getContext("2d");n.drawImage(this.img,0,0);for(var i=n.getImageData(0,0,1,t).data,o=0,a=t,s=t;s>o;){0===i[4*(s-1)+3]?a=s:o=s,s=a+o>>1}var r=s/t;return 0===r?1:r}},{key:"getOrientation",value:function(t,e){var n=new FileReader;n.onload=function(t){var n=new DataView(t.target.result);if(65496!=n.getUint16(0,!1))return e(-2);for(var i=n.byteLength,o=2;othis.temp.height?(o=this.scaleHeight/this.height,this.scaleHeight=this.height*e,this.scaleWidth=this.scaleHeight*this.temp.width/this.temp.height):(o=this.scaleWidth/this.width,this.scaleWidth=this.width*e,this.scaleHeight=this.scaleWidth*this.temp.height/this.temp.width),this.scaleX=n*e/o+this.width/2,this.scaleY=i*e/o+this.height/2,this.limit(),this.cropCanvas(),this.cropImage.style.width=this.scaleWidth+"px",this.cropImage.style.height=this.scaleHeight+"px",this.cropImage.style.left=this.scaleX+"px",this.cropImage.style.top=this.scaleY+"px"}},{key:"limit",value:function(){this.scaleX<-(this.scaleWidth-this.width)&&(this.scaleX=-(this.scaleWidth-this.width)),this.scaleY<-(this.scaleHeight-this.height)&&(this.scaleY=-(this.scaleHeight-this.height)),this.scaleX>0&&(this.scaleX=0),this.scaleY>0&&(this.scaleY=0),this.scaleHeight===this.height&&(this.scaleY=0),this.scaleWidth===this.width&&(this.scaleX=0)}},{key:"touchData",value:function(t){if(!(t.touches.length<2)){var e=t.touches[0].pageX,n=t.touches[1].pageX,i=e<=n?(n-e)/2+e:(e-n)/2+n,o=t.touches[0].pageY-this.scrollbar.scrollTop,a=t.touches[1].pageY-this.scrollbar.scrollTop,s=o<=a?(a-o)/2+o:(o-a)/2+a;return{length:Math.round(Math.sqrt(Math.pow(e-n,2)+Math.pow(o-a,2))),x:Math.round(i),y:Math.round(s)}}}},{key:"dataURLtoBlob",value:function(t){for(var e=t.split(","),n=e[0].match(/:(.*?);/)[1],i=atob(e[1]),o=i.length,a=new Uint8Array(o);o--;)a[o]=i.charCodeAt(o);return URL.createObjectURL(new Blob([a],{type:n}))}}]),n}()}();
2 |
--------------------------------------------------------------------------------
/src/crop.js:
--------------------------------------------------------------------------------
1 | export default class crop {
2 | constructor(options = {}) {
3 | this.width = options.width || 400;
4 | this.height = options.height || 400;
5 | this.quality = options.quality || 0.8;
6 | this.success = options.success;
7 | this.style();
8 | const html = `
9 |
10 |
11 |
12 |
13 |
14 |
21 |
22 | OK
23 | `;
24 | this.layout = document.createElement('DIV');
25 | this.layout.classList.add("crop");
26 | this.layout.classList.add("active");
27 | this.layout.innerHTML = html;
28 | document.querySelector("body").appendChild(this.layout);
29 |
30 | this.input = document.querySelector(options.input || ".crop-file");
31 | this.crop = document.querySelector(options.crop || ".crop");
32 | this.cropWall = document.querySelector(options.wall || ".crop-wall");
33 | this.cropImage = document.querySelector(options.image || ".crop-image");
34 | this.cropClose = document.querySelector(options.close || ".crop-close");
35 | this.cropOk = document.querySelector(options.ok || ".crop-ok");
36 | this.cropZoom = document.querySelector(options.zoom || ".crop-zoom");
37 |
38 | this.crop.style.width = this.width + "px";
39 | this.crop.style.height = this.height + "px";
40 | this.cropWall.style.width = this.width + "px";
41 | this.cropWall.style.height = this.height + "px";
42 |
43 | this.input.addEventListener("change", (e) => {
44 | this.files = e.target.files || e.dataTransfer.files;
45 | if (this.files.length == 0) return false;
46 | this.getOrientation(this.files[0], (o) => {
47 | this.orientation = o;
48 | var reader = new FileReader();
49 | reader.onload = (file) => {
50 | this.open(this.dataURLtoBlob(file.target.result));
51 | };
52 | reader.readAsDataURL(this.files[0]);
53 | })
54 | });
55 | if (options.url != undefined) {
56 | this.open(options.url);
57 | }
58 |
59 | this.cropClose.addEventListener("click", () => {
60 | this.layout.parentNode.removeChild(this.layout);
61 | });
62 |
63 | // 渲染到canvas
64 | this.canvas = document.createElement("canvas")
65 | this.canvas.width = this.width
66 | this.canvas.height = this.height
67 | this.canvas.setAttribute("style", "position:absolute;left:0;top:0;opacity:0;pointer-events:none;")
68 | this.context = this.canvas.getContext("2d")
69 |
70 | this.layout.appendChild(this.canvas);
71 |
72 | }
73 |
74 | style() {
75 | if (document.querySelector("#cropStyle")) {
76 | return;
77 | }
78 | const css = `
79 | .crop {
80 | position: absolute;
81 | left: 50%;
82 | top: 50%;
83 | transform: translate3d(-50%, -50%, 0);
84 | width: 400px;
85 | height: 400px;
86 | background: #000;
87 | box-shadow: 0 0 20px 2px rgba(0, 0, 0, .5);
88 | z-index: 20;
89 | transition: all .2s cubic-bezier(0.99, 0.01, 0.22, 0.94);
90 | }
91 |
92 | .crop.loading::after {
93 | content: "LOADING";
94 | position: absolute;
95 | left: 0;
96 | right: 0;
97 | top: 0;
98 | bottom: 0;
99 | display: flex;
100 | align-items: center;
101 | justify-content: center;
102 | font-size: 10px;
103 | background-color: rgba(0, 0, 0, .5);
104 | color: #666;
105 | }
106 | .crop-wall {
107 | position: relative;
108 | height: 100%;
109 | overflow: hidden;
110 | }
111 | .crop-wall img{
112 | user-select:none;
113 | }
114 | .crop-mask{
115 | pointer-events:none;
116 | position:absolute;
117 | left:0;
118 | right:0;
119 | top:0;
120 | bottom:0;
121 | }
122 | .crop-mask::before{
123 | content:"";
124 | position:absolute;
125 | left:0;
126 | right:0;
127 | top:0;
128 | height:120px;
129 | background-image: linear-gradient(0deg, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 0.8) 100%);
130 | }
131 | .crop-mask::after{
132 | content:"";
133 | position:absolute;
134 | left:0;
135 | right:0;
136 | bottom:0;
137 | height:120px;
138 | background-image: linear-gradient(0deg, rgba(0, 0, 0, 0.8) 0%, rgba(0, 0, 0, 0) 100%);
139 | }
140 | .crop-file {
141 | position: absolute;
142 | left: 15px;
143 | top: 15px;
144 | transition: .3s;
145 | cursor: pointer;
146 | }
147 |
148 | .crop-file:hover {
149 | transform: scale(1.2);
150 | }
151 |
152 | .crop-file svg {
153 | width: 22px;
154 | height: 22px;
155 | }
156 |
157 | .crop-file input {
158 | position: absolute;
159 | width: 100%;
160 | height: 100%;
161 | opacity: 0;
162 | cursor: pointer;
163 | }
164 | .crop.active .crop-file {
165 | position: absolute;
166 | left: 50%;
167 | top: 50%;
168 | transform: translate(-50%, -50%);
169 | transition: 0s;
170 | }
171 | .crop.loading .crop-file {
172 | opacity: 0;
173 | }
174 | .crop.active .crop-file svg {
175 | width: 50px;
176 | height: 50px;
177 | }
178 | .crop.active .crop-file svg path {
179 | fill: #333;
180 | }
181 | .crop-ok {
182 | position: absolute;
183 | right: 10px;
184 | bottom: 10px;
185 | width: 66px;
186 | height: 26px;
187 | border-radius: 40px;
188 | text-align: center;
189 | font-size: 10px;
190 | font-family: arial;
191 | line-height: 26px;
192 | border: 2px solid #fff;
193 | color: #fff;
194 | transition: all .2s ease-in-out;
195 | cursor: pointer;
196 | opacity: 0;
197 | }
198 | .crop-ok.show {
199 | opacity: 1;
200 | }
201 | .crop-ok:hover {
202 | background-color: rgba(255, 255, 255, 0.2);
203 | }
204 | .crop-close {
205 | position: absolute;
206 | top: 15px;
207 | right: 15px;
208 | width: 30px;
209 | height: 30px;
210 | cursor: pointer;
211 | transition: .3s;
212 | transform: scale(0.8);
213 | }
214 | .crop-close:hover {
215 | transform: scale(1);
216 | }
217 | .crop-close:before {
218 | position: absolute;
219 | content: '';
220 | width: 30px;
221 | height: 2px;
222 | background: white;
223 | transform: rotate(45deg);
224 | top: 14px;
225 | left: 0px;
226 | border-radius: 2px;
227 | }
228 | .crop-close:after {
229 | content: '';
230 | position: absolute;
231 | width: 30px;
232 | height: 2px;
233 | background: white;
234 | transform: rotate(-45deg);
235 | top: 14px;
236 | left: 0px;
237 | border-radius: 2px;
238 | }
239 | .crop-zoom {
240 | position: absolute;
241 | left: 10px;
242 | bottom: 20px;
243 | width: 100px;
244 | opacity: 0;
245 | transition: .3s;
246 | }
247 | .crop-zoom.show {
248 | opacity: 1;
249 | }
250 | input[type=range] {
251 | -webkit-appearance: none;
252 | }
253 | input[type=range]:focus {
254 | outline: none;
255 | }
256 | input[type=range]::-webkit-slider-runnable-track {
257 | width: 100%;
258 | height: 2px;
259 | border-radius: 2px;
260 | background: #fff;
261 | border: none;
262 | }
263 | input[type=range]::-webkit-slider-thumb {
264 | height: 16px;
265 | width: 16px;
266 | border-radius: 10px;
267 | background: #fff;
268 | cursor: pointer;
269 | -webkit-appearance: none;
270 | margin-top: -8px;
271 | transition: .3s;
272 | }
273 | input[type=range]::-webkit-slider-thumb:hover {
274 | transform: scale(1.2);
275 | }
276 | input[type=range]::-moz-range-track {
277 | width: 100%;
278 | height: 2px;
279 | border-radius: 2px;
280 | background: #fff;
281 | border: none;
282 | }
283 | input[type=range]::-moz-range-thumb {
284 | height: 16px;
285 | width: 16px;
286 | border-radius: 10px;
287 | background: #fff;
288 | cursor: pointer;
289 | -moz-appearance: none;
290 | margin-top: -8px;
291 | transition: .3s;
292 | }
293 |
294 | input[type=range]::-ms-track {
295 | width: 100%;
296 | height: 2px;
297 | border-radius: 2px;
298 | background: #fff;
299 | border: none;
300 | }
301 |
302 | input[type=range]::-ms-fill-lower {
303 | background: rgba(0, 0, 0, 0.5);
304 | border: 0px solid rgba(200, 200, 200, 0.2);
305 | border-radius: 0px;
306 | box-shadow: 0px 0px 0px rgba(0, 0, 0, 0), 0px 0px 0px rgba(13, 13, 13, 0);
307 | }
308 |
309 | input[type=range]::-ms-fill-upper {
310 | background: rgba(0, 0, 0, 0.5);
311 | border: 0px solid rgba(200, 200, 200, 0.2);
312 | border-radius: 0px;
313 | box-shadow: 0px 0px 0px rgba(0, 0, 0, 0), 0px 0px 0px rgba(13, 13, 13, 0);
314 | }
315 |
316 | input[type=range]::-ms-thumb {
317 | height: 16px;
318 | width: 16px;
319 | border-radius: 10px;
320 | background: #fff;
321 | cursor: pointer;
322 | margin-top: -8px;
323 | transition: .3s;
324 | }
325 |
326 | input[type=range]:focus::-ms-fill-lower {
327 | background: rgba(0, 0, 0, 0.5);
328 | }
329 |
330 | input[type=range]:focus::-ms-fill-upper {
331 | background: rgba(0, 0, 0, 0.5);
332 | }
333 | `;
334 | const node = document.createElement("style");
335 | node.type = "text/css";
336 | node.id = "cropStyle"
337 | if (node.styleSheet) {
338 | node.styleSheet.cssText = css.replace(/\s/, "");
339 | } else {
340 | node.innerHTML = css.replace(/\s/, "");
341 | }
342 | document.getElementsByTagName("head")[0].appendChild(node);
343 | }
344 | // 打开裁剪
345 | open(url) {
346 | const that = this;
347 | this.isMobile = /ios|android/.test(navigator.userAgent.toLowerCase());
348 | this.beginX = 0;
349 | this.beginY = 0;
350 | this.scaleX = 0;
351 | this.scaleY = 0;
352 | this.scaleHeight = 0;
353 | this.scaleWidth = 0;
354 | this.isDrag = false;
355 |
356 | this.crop.classList.add("loading");
357 |
358 | this.img = new Image();
359 | this.img.crossOrigin = 'anonymous';
360 | this.img.onload = () => {
361 | this.crop.classList.remove("loading");
362 | this.crop.classList.remove("active");
363 | this.cropZoom.classList.add("show");
364 | this.cropOk.classList.add("show");
365 | // alert(this.img.naturalWidth + "/" + this.img.naturalHeight)
366 | // 按原始图片比例优先使用小值设置最低值
367 | this.temp = {
368 | width: this.img.naturalWidth,
369 | height: this.img.naturalHeight
370 | };
371 | if (this.orientation == 6) {
372 | this.temp.width = this.img.naturalHeight;
373 | this.temp.height = this.img.naturalWidth;
374 | }
375 | if (this.temp.width > this.temp.height) {
376 | this.scaleHeight = this.height;
377 | this.scaleWidth = Math.round(this.scaleHeight * this.temp.width / this.temp.height);
378 | } else {
379 | this.scaleWidth = this.width;
380 | this.scaleHeight = Math.round(this.scaleWidth * this.temp.height / this.temp.width);
381 | }
382 |
383 | // console.log(scaleX,scaleY,scaleWidth,scaleHeight);
384 | this.scaleX = -Math.round((this.scaleWidth - this.width) / 2);
385 | this.scaleY = -Math.round((this.scaleHeight - this.height) / 2);
386 | if (this.orientation == 6) {
387 | this.fixedOrientation(this.img, this.temp.width, this.temp.height, (src) => {
388 | this.cropImage.setAttribute("src", src);
389 | })
390 | } else {
391 |
392 | this.cropImage.setAttribute("src", url);
393 | }
394 |
395 | this.crop.style.width = this.width + "px";
396 | this.crop.style.height = this.height + "px";
397 | this.cropWall.style.width = this.width + "px";
398 | this.cropWall.style.height = this.height + "px";
399 |
400 | this.cropImage.style.position = "absolute";
401 | this.cropImage.style.left = this.scaleX + "px";
402 | this.cropImage.style.top = this.scaleY + "px";
403 | this.cropImage.style.width = this.scaleWidth + "px";
404 | this.cropImage.style.height = this.scaleHeight + "px";
405 | this.cropImage.style.userSelect = "none";
406 | this.cropZoom.value = 100;
407 |
408 | this.cropCanvas();
409 | if (!this.hasEvent) {
410 | let okHander = () => {
411 | let data = this.canvas.toDataURL("image/jpeg", this.quality);
412 | this.success && this.success(data.substr(23));
413 | this.layout.parentNode.removeChild(this.layout);
414 | }
415 |
416 | this.cropZoom.addEventListener("input", function (e) {
417 | that.zoom(e);
418 | });
419 | this.cropOk.addEventListener("click", okHander);
420 |
421 | this.cropImage.addEventListener(that.isMobile ? "touchstart" : "mousedown", function (e) {
422 | e.preventDefault();
423 | that.isDrag = true
424 | that.beginX = e.pageX - e.target.offsetLeft
425 | that.beginY = e.pageY - e.target.offsetTop
426 | // 拖动鼠标
427 | const move = function (e) {
428 | // console.log("move",e)
429 | // e.preventDefault();
430 |
431 | if (!that.isDrag) return false
432 | // 放大
433 | if (that.isMobile) {
434 | if (e.touches.length == 2) {
435 | that.scaleLength = that.touchData(e).length
436 | that.scale = Math.min(200, Math.max(100, that.scaleLength / that.beginLength * 100))
437 | this.zoom()
438 | } else {
439 | that.scaleX = e.pageX - beginX
440 | that.scaleY = e.pageY - beginY
441 |
442 | }
443 | } else {
444 | that.scaleX = e.pageX - that.beginX
445 | that.scaleY = e.pageY - that.beginY
446 | }
447 | that.limit()
448 | that.cropImage.style.width = that.scaleWidth + "px";
449 | that.cropImage.style.height = that.scaleHeight + "px";
450 | that.cropImage.style.left = that.scaleX + "px";
451 | that.cropImage.style.top = that.scaleY + "px";
452 | }
453 |
454 | // 放开那个鼠标
455 | const end = function (e) {
456 | // e.preventDefault();
457 | that.isDrag = false
458 | if (that.isMobile) {
459 | document.removeEventListener('touchmove', move, false)
460 | document.removeEventListener('touchend', end, false)
461 | } else {
462 | document.removeEventListener('mousemove', move, false)
463 | document.removeEventListener('mouseup', end, false)
464 | }
465 | }
466 | if (that.isMobile) {
467 | if (e.touches.length == 2) {
468 | that.beginLength = that.beginLength == 0 ? that.touchData(e).length : that.beginLength
469 | }
470 | document.addEventListener('touchmove', move);
471 | document.addEventListener('touchend', end);
472 |
473 | } else {
474 | document.addEventListener('mousemove', move);
475 | document.addEventListener('mouseup', end);
476 | }
477 |
478 | });
479 | this.hasEvent = true;
480 | }
481 | }
482 | this.img.src = url;
483 | }
484 | // 粘贴到canvas
485 | cropCanvas() {
486 | this.context.clearRect(0, 0, this.width, this.height)
487 | let sx = Math.round(Math.abs(this.scaleX) * this.img.naturalWidth / this.scaleWidth) //图像源x坐标
488 | let sy = Math.round(Math.abs(this.scaleY) * this.img.naturalHeight / this.scaleHeight) //图像源y坐标
489 | let sw = (this.width * this.img.naturalWidth / this.scaleWidth) >> 0
490 | let sh = (this.height * this.img.naturalHeight / this.scaleHeight) >> 0
491 | this.context.drawImage(this.img, sx, sy, sw, sh, 0, 0, this.width, this.height / this.cropRatio())
492 | }
493 |
494 | // http://stackoverflow.com/questions/11929099/html5-canvas-drawimage-ratio-bug-ios
495 | // 修正 iPhone 上传的方向问题
496 | cropRatio() {
497 | var iw = this.img.naturalWidth, ih = this.img.naturalHeight;
498 | var canvas = document.createElement('canvas');
499 | canvas.width = 1;
500 | canvas.height = ih;
501 | var ctx = canvas.getContext('2d');
502 | ctx.drawImage(this.img, 0, 0);
503 | var data = ctx.getImageData(0, 0, 1, ih).data;
504 | // search image edge pixel position in case it is squashed vertically.
505 | var sy = 0;
506 | var ey = ih;
507 | var py = ih;
508 | while (py > sy) {
509 | var alpha = data[(py - 1) * 4 + 3];
510 | if (alpha === 0) {
511 | ey = py;
512 | } else {
513 | sy = py;
514 | }
515 | py = (ey + sy) >> 1;
516 | }
517 | var ratio = (py / ih);
518 | return (ratio === 0) ? 1 : ratio;
519 | }
520 |
521 | getOrientation(file, callback) {
522 | var reader = new FileReader();
523 | reader.onload = function (e) {
524 |
525 | var view = new DataView(e.target.result);
526 | if (view.getUint16(0, false) != 0xFFD8) return callback(-2);
527 | var length = view.byteLength, offset = 2;
528 | while (offset < length) {
529 | var marker = view.getUint16(offset, false);
530 | offset += 2;
531 | if (marker == 0xFFE1) {
532 | if (view.getUint32(offset += 2, false) != 0x45786966) return callback(-1);
533 | var little = view.getUint16(offset += 6, false) == 0x4949;
534 | offset += view.getUint32(offset + 4, little);
535 | var tags = view.getUint16(offset, little);
536 | offset += 2;
537 | for (var i = 0; i < tags; i++)
538 | if (view.getUint16(offset + (i * 12), little) == 0x0112)
539 | return callback(view.getUint16(offset + (i * 12) + 8, little));
540 | }
541 | else if ((marker & 0xFF00) != 0xFF00) break;
542 | else offset += view.getUint16(offset, false);
543 | }
544 | return callback(-1);
545 | };
546 | reader.readAsArrayBuffer(file.slice(0, 64 * 1024));
547 | }
548 |
549 | //图片压缩
550 | fixedOrientation(img, width, height, callback) {
551 | var canvas, ctx;
552 | canvas = document.createElement('canvas');
553 | canvas.width = width;
554 | canvas.height = height;
555 | ctx = canvas.getContext("2d");
556 | //如果图片方向等于6 ,则旋转矫正,反之则不做处理
557 | if (this.orientation == 6) {
558 | ctx.save();
559 | ctx.translate(width / 2, height / 2);
560 | ctx.rotate(90 * Math.PI / 180);
561 | ctx.drawImage(img, 0 - height / 2, 0 - width / 2, height, width);
562 | ctx.restore();
563 | } else {
564 | ctx.drawImage(img, 0, 0, width, height);
565 | }
566 | canvas.toBlob(function (blob) {
567 | let url = URL.createObjectURL(blob);
568 | callback && callback(url);
569 | });
570 |
571 | }
572 | // 放大缩小
573 | zoom(e) {
574 | let s = parseInt(e.target.value) / 100;
575 | let osx = this.scaleX - this.width / 2;
576 | let osy = this.scaleY - this.height / 2;
577 | let os;
578 | // 按原始图片比例优先使用小值设置最低值
579 | if (this.temp.width > this.temp.height) {
580 | os = this.scaleHeight / this.height;
581 | this.scaleHeight = this.height * s;
582 | this.scaleWidth = this.scaleHeight * this.temp.width / this.temp.height;
583 | } else {
584 | os = this.scaleWidth / this.width;
585 | this.scaleWidth = this.width * s;
586 | this.scaleHeight = this.scaleWidth * this.temp.height / this.temp.width;
587 | }
588 | this.scaleX = (osx) * s / os + this.width / 2;
589 | this.scaleY = (osy) * s / os + this.height / 2;
590 | this.limit();
591 | this.cropCanvas()
592 | this.cropImage.style.width = this.scaleWidth + "px";
593 | this.cropImage.style.height = this.scaleHeight + "px";
594 | this.cropImage.style.left = this.scaleX + "px";
595 | this.cropImage.style.top = this.scaleY + "px";
596 | }
597 | // 限制拖动检测边缘
598 | limit() {
599 | if (this.scaleX < -(this.scaleWidth - this.width)) this.scaleX = -(this.scaleWidth - this.width)
600 | if (this.scaleY < -(this.scaleHeight - this.height)) this.scaleY = -(this.scaleHeight - this.height)
601 | if (this.scaleX > 0) this.scaleX = 0
602 | if (this.scaleY > 0) this.scaleY = 0
603 | if (this.scaleHeight === this.height) this.scaleY = 0
604 | if (this.scaleWidth === this.width) this.scaleX = 0
605 | }
606 |
607 | // 获取多点触控
608 | touchData(e) {
609 | if (e.touches.length < 2) return
610 | let x1 = e.touches[0].pageX
611 | let x2 = e.touches[1].pageX
612 | let x3 = (x1 <= x2 ? (x2 - x1) / 2 + x1 : (x1 - x2) / 2 + x2)
613 | let y1 = e.touches[0].pageY - this.scrollbar.scrollTop
614 | let y2 = e.touches[1].pageY - this.scrollbar.scrollTop
615 | let y3 = (y1 <= y2 ? (y2 - y1) / 2 + y1 : (y1 - y2) / 2 + y2)
616 | return {
617 | length: Math.round(Math.sqrt(Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2))),
618 | x: Math.round(x3),
619 | y: Math.round(y3)
620 | }
621 | }
622 |
623 | dataURLtoBlob(dataurl) {
624 | let arr = dataurl.split(',');
625 | let mime = arr[0].match(/:(.*?);/)[1];
626 | let bstr = atob(arr[1]);
627 | let n = bstr.length, u8arr = new Uint8Array(n);
628 | while (n--) {
629 | u8arr[n] = bstr.charCodeAt(n);
630 | }
631 | return URL.createObjectURL(new Blob([u8arr], { type: mime }));
632 | }
633 |
634 | }
--------------------------------------------------------------------------------
/dist/crop.js:
--------------------------------------------------------------------------------
1 | (function (global, factory) {
2 | typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
3 | typeof define === 'function' && define.amd ? define(factory) :
4 | (global.crop = factory());
5 | }(this, (function () { 'use strict';
6 |
7 | var classCallCheck = function (instance, Constructor) {
8 | if (!(instance instanceof Constructor)) {
9 | throw new TypeError("Cannot call a class as a function");
10 | }
11 | };
12 |
13 | var createClass = function () {
14 | function defineProperties(target, props) {
15 | for (var i = 0; i < props.length; i++) {
16 | var descriptor = props[i];
17 | descriptor.enumerable = descriptor.enumerable || false;
18 | descriptor.configurable = true;
19 | if ("value" in descriptor) descriptor.writable = true;
20 | Object.defineProperty(target, descriptor.key, descriptor);
21 | }
22 | }
23 |
24 | return function (Constructor, protoProps, staticProps) {
25 | if (protoProps) defineProperties(Constructor.prototype, protoProps);
26 | if (staticProps) defineProperties(Constructor, staticProps);
27 | return Constructor;
28 | };
29 | }();
30 |
31 | var crop = function () {
32 | function crop() {
33 | var _this = this;
34 |
35 | var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
36 | classCallCheck(this, crop);
37 |
38 | this.width = options.width || 400;
39 | this.height = options.height || 400;
40 | this.quality = options.quality || 0.8;
41 | this.success = options.success;
42 | this.style();
43 | var html = "\n \n
\n
\n
\n \n \n
\n OK
\n ";
44 | this.layout = document.createElement('DIV');
45 | this.layout.classList.add("crop");
46 | this.layout.classList.add("active");
47 | this.layout.innerHTML = html;
48 | document.querySelector("body").appendChild(this.layout);
49 |
50 | this.input = document.querySelector(options.input || ".crop-file");
51 | this.crop = document.querySelector(options.crop || ".crop");
52 | this.cropWall = document.querySelector(options.wall || ".crop-wall");
53 | this.cropImage = document.querySelector(options.image || ".crop-image");
54 | this.cropClose = document.querySelector(options.close || ".crop-close");
55 | this.cropOk = document.querySelector(options.ok || ".crop-ok");
56 | this.cropZoom = document.querySelector(options.zoom || ".crop-zoom");
57 | this.crop.style.width = this.width + "px";
58 | this.crop.style.height = this.height + "px";
59 | this.cropWall.style.width = this.width + "px";
60 | this.cropWall.style.height = this.height + "px";
61 | this.input.addEventListener("change", function (e) {
62 | _this.files = e.target.files || e.dataTransfer.files;
63 | if (_this.files.length == 0) return false;
64 | _this.getOrientation(_this.files[0], function (o) {
65 | _this.orientation = o;
66 | var reader = new FileReader();
67 | reader.onload = function (file) {
68 | _this.open(_this.dataURLtoBlob(file.target.result));
69 | };
70 | reader.readAsDataURL(_this.files[0]);
71 | });
72 | });
73 | if (options.url != undefined) {
74 | this.open(options.url);
75 | }
76 |
77 | this.cropClose.addEventListener("click", function () {
78 | _this.layout.parentNode.removeChild(_this.layout);
79 | });
80 |
81 | // 渲染到canvas
82 | this.canvas = document.createElement("canvas");
83 | this.canvas.width = this.width;
84 | this.canvas.height = this.height;
85 | this.canvas.setAttribute("style", "position:absolute;left:0;top:0;opacity:0;pointer-events:none;");
86 | this.context = this.canvas.getContext("2d");
87 |
88 | this.layout.appendChild(this.canvas);
89 | }
90 |
91 | createClass(crop, [{
92 | key: "style",
93 | value: function style() {
94 | if (document.querySelector("#cropStyle")) {
95 | return;
96 | }
97 | var css = "\n .crop {\n position: absolute;\n left: 50%;\n top: 50%;\n transform: translate3d(-50%, -50%, 0);\n width: 400px;\n height: 400px;\n background: #000;\n box-shadow: 0 0 20px 2px rgba(0, 0, 0, .5);\n z-index: 20;\n transition: all .2s cubic-bezier(0.99, 0.01, 0.22, 0.94);\n }\n\n .crop.loading::after {\n content: \"LOADING\";\n position: absolute;\n left: 0;\n right: 0;\n top: 0;\n bottom: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 10px;\n background-color: rgba(0, 0, 0, .5);\n color: #666;\n }\n\n .crop-wall {\n position: relative;\n height: 100%;\n overflow: hidden;\n }\n .crop-wall img{\n user-select:none;\n }\n .crop-mask{\n pointer-events:none;\n position:absolute;\n left:0;\n right:0;\n top:0;\n bottom:0;\n }\n .crop-mask::before{\n content:\"\";\n position:absolute;\n left:0;\n right:0;\n top:0;\n height:120px;\n background-image: linear-gradient(0deg, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 0.8) 100%);\n }\n .crop-mask::after{\n content:\"\";\n position:absolute;\n left:0;\n right:0;\n bottom:0;\n height:120px;\n background-image: linear-gradient(0deg, rgba(0, 0, 0, 0.8) 0%, rgba(0, 0, 0, 0) 100%);\n }\n .crop-file {\n position: absolute;\n left: 15px;\n top: 15px;\n transition: .3s;\n cursor: pointer;\n }\n\n .crop-file:hover {\n transform: scale(1.2);\n }\n\n .crop-file svg {\n width: 22px;\n height: 22px;\n }\n\n .crop-file input {\n position: absolute;\n width: 100%;\n height: 100%;\n opacity: 0;\n cursor: pointer;\n }\n\n .crop.active .crop-file {\n position: absolute;\n left: 50%;\n top: 50%;\n transform: translate(-50%, -50%);\n transition: 0s;\n }\n\n .crop.loading .crop-file {\n opacity: 0;\n\n }\n\n .crop.active .crop-file svg {\n width: 50px;\n height: 50px;\n }\n\n .crop.active .crop-file svg path {\n fill: #333;\n }\n\n .crop-ok {\n position: absolute;\n right: 10px;\n bottom: 10px;\n width: 66px;\n height: 26px;\n border-radius: 40px;\n text-align: center;\n font-size: 10px;\n font-family: arial;\n line-height: 26px;\n border: 2px solid #fff;\n color: #fff;\n transition: all .2s ease-in-out;\n cursor: pointer;\n opacity: 0;\n }\n\n .crop-ok.show {\n opacity: 1;\n }\n\n .crop-ok:hover {\n background-color: rgba(255, 255, 255, 0.2);\n }\n\n .crop-close {\n position: absolute;\n top: 15px;\n right: 15px;\n width: 30px;\n height: 30px;\n cursor: pointer;\n transition: .3s;\n transform: scale(0.8);\n }\n\n .crop-close:hover {\n transform: scale(1);\n }\n\n .crop-close:before {\n position: absolute;\n content: '';\n width: 30px;\n height: 2px;\n background: white;\n transform: rotate(45deg);\n top: 14px;\n left: 0px;\n border-radius: 2px;\n }\n\n .crop-close:after {\n content: '';\n position: absolute;\n width: 30px;\n height: 2px;\n background: white;\n transform: rotate(-45deg);\n top: 14px;\n left: 0px;\n border-radius: 2px;\n }\n\n .crop-zoom {\n position: absolute;\n left: 10px;\n bottom: 20px;\n width: 100px;\n opacity: 0;\n transition: .3s;\n }\n\n .crop-zoom.show {\n opacity: 1;\n }\n\n input[type=range] {\n -webkit-appearance: none;\n\n }\n\n input[type=range]:focus {\n outline: none;\n }\n\n input[type=range]::-webkit-slider-runnable-track {\n width: 100%;\n height: 2px;\n border-radius: 2px;\n background: #fff;\n border: none;\n\n }\n\n input[type=range]::-webkit-slider-thumb {\n height: 16px;\n width: 16px;\n border-radius: 10px;\n background: #fff;\n cursor: pointer;\n -webkit-appearance: none;\n margin-top: -8px;\n transition: .3s;\n }\n\n input[type=range]::-webkit-slider-thumb:hover {\n transform: scale(1.2);\n }\n\n input[type=range]::-moz-range-track {\n width: 100%;\n height: 2px;\n background: #000;\n }\n\n input[type=range]::-moz-range-thumb {\n height: 16px;\n width: 16px;\n border-radius: 8px;\n border: 2px solid #efb708;\n background: #ffffff;\n cursor: pointer;\n }\n\n input[type=range]::-ms-track {\n width: 100%;\n height: 1px;\n cursor: pointer;\n background: transparent;\n border-color: transparent;\n color: transparent;\n }\n\n input[type=range]::-ms-fill-lower {\n background: rgba(0, 0, 0, 0.5);\n border: 0px solid rgba(200, 200, 200, 0.2);\n border-radius: 0px;\n box-shadow: 0px 0px 0px rgba(0, 0, 0, 0), 0px 0px 0px rgba(13, 13, 13, 0);\n }\n\n input[type=range]::-ms-fill-upper {\n background: rgba(0, 0, 0, 0.5);\n border: 0px solid rgba(200, 200, 200, 0.2);\n border-radius: 0px;\n box-shadow: 0px 0px 0px rgba(0, 0, 0, 0), 0px 0px 0px rgba(13, 13, 13, 0);\n }\n\n input[type=range]::-ms-thumb {\n height: 16px;\n width: 16px;\n border-radius: 8px;\n background: #ffffff;\n cursor: pointer;\n height: 1px;\n }\n\n input[type=range]:focus::-ms-fill-lower {\n background: rgba(0, 0, 0, 0.5);\n }\n\n input[type=range]:focus::-ms-fill-upper {\n background: rgba(0, 0, 0, 0.5);\n }\n ";
98 | var node = document.createElement("style");
99 | node.type = "text/css";
100 | node.id = "cropStyle";
101 | if (node.styleSheet) {
102 | node.styleSheet.cssText = css.replace(/\s/, "");
103 | } else {
104 | node.innerHTML = css.replace(/\s/, "");
105 | }
106 | document.getElementsByTagName("head")[0].appendChild(node);
107 | }
108 | // 打开裁剪
109 |
110 | }, {
111 | key: "open",
112 | value: function open(url) {
113 | var _this2 = this;
114 |
115 | var that = this;
116 | this.isMobile = /ios|android/.test(navigator.userAgent.toLowerCase());
117 | this.beginX = 0;
118 | this.beginY = 0;
119 | this.scaleX = 0;
120 | this.scaleY = 0;
121 | this.scaleHeight = 0;
122 | this.scaleWidth = 0;
123 | this.isDrag = false;
124 |
125 | this.crop.classList.add("loading");
126 |
127 | this.img = new Image();
128 | this.img.crossOrigin = 'anonymous';
129 | this.img.onload = function () {
130 | _this2.crop.classList.remove("loading");
131 | _this2.crop.classList.remove("active");
132 | _this2.cropZoom.classList.add("show");
133 | _this2.cropOk.classList.add("show");
134 | // alert(this.img.naturalWidth + "/" + this.img.naturalHeight)
135 | // 按原始图片比例优先使用小值设置最低值
136 | _this2.temp = {
137 | width: _this2.img.naturalWidth,
138 | height: _this2.img.naturalHeight
139 | };
140 | if (_this2.orientation == 6) {
141 | _this2.temp.width = _this2.img.naturalHeight;
142 | _this2.temp.height = _this2.img.naturalWidth;
143 | }
144 | if (_this2.temp.width > _this2.temp.height) {
145 | _this2.scaleHeight = _this2.height;
146 | _this2.scaleWidth = Math.round(_this2.scaleHeight * _this2.temp.width / _this2.temp.height);
147 | } else {
148 | _this2.scaleWidth = _this2.width;
149 | _this2.scaleHeight = Math.round(_this2.scaleWidth * _this2.temp.height / _this2.temp.width);
150 | }
151 |
152 | // console.log(scaleX,scaleY,scaleWidth,scaleHeight);
153 | _this2.scaleX = -Math.round((_this2.scaleWidth - _this2.width) / 2);
154 | _this2.scaleY = -Math.round((_this2.scaleHeight - _this2.height) / 2);
155 | if (_this2.orientation == 6) {
156 | _this2.fixedOrientation(_this2.img, _this2.temp.width, _this2.temp.height, function (src) {
157 | _this2.cropImage.setAttribute("src", src);
158 | });
159 | } else {
160 |
161 | _this2.cropImage.setAttribute("src", url);
162 | }
163 |
164 | _this2.crop.style.width = _this2.width + "px";
165 | _this2.crop.style.height = _this2.height + "px";
166 | _this2.cropWall.style.width = _this2.width + "px";
167 | _this2.cropWall.style.height = _this2.height + "px";
168 |
169 | _this2.cropImage.style.position = "absolute";
170 | _this2.cropImage.style.left = _this2.scaleX + "px";
171 | _this2.cropImage.style.top = _this2.scaleY + "px";
172 | _this2.cropImage.style.width = _this2.scaleWidth + "px";
173 | _this2.cropImage.style.height = _this2.scaleHeight + "px";
174 | _this2.cropImage.style.userSelect = "none";
175 | _this2.cropZoom.value = 100;
176 |
177 | _this2.cropCanvas();
178 | if (!_this2.hasEvent) {
179 | var okHander = function okHander() {
180 | var data = _this2.canvas.toDataURL("image/jpeg", _this2.quality);
181 | _this2.success && _this2.success(data.substr(23));
182 | _this2.layout.parentNode.removeChild(_this2.layout);
183 | };
184 |
185 | _this2.cropZoom.addEventListener("input", function (e) {
186 | that.zoom(e);
187 | });
188 | _this2.cropOk.addEventListener("click", okHander);
189 |
190 | _this2.cropImage.addEventListener(that.isMobile ? "touchstart" : "mousedown", function (e) {
191 | e.preventDefault();
192 | that.isDrag = true;
193 | that.beginX = e.pageX - e.target.offsetLeft;
194 | that.beginY = e.pageY - e.target.offsetTop;
195 | // 拖动鼠标
196 | var move = function move(e) {
197 | // console.log("move",e)
198 | // e.preventDefault();
199 |
200 | if (!that.isDrag) return false;
201 | // 放大
202 | if (that.isMobile) {
203 | if (e.touches.length == 2) {
204 | that.scaleLength = that.touchData(e).length;
205 | that.scale = Math.min(200, Math.max(100, that.scaleLength / that.beginLength * 100));
206 | this.zoom();
207 | } else {
208 | that.scaleX = e.pageX - beginX;
209 | that.scaleY = e.pageY - beginY;
210 | }
211 | } else {
212 | that.scaleX = e.pageX - that.beginX;
213 | that.scaleY = e.pageY - that.beginY;
214 | }
215 | that.limit();
216 | that.cropImage.style.width = that.scaleWidth + "px";
217 | that.cropImage.style.height = that.scaleHeight + "px";
218 | that.cropImage.style.left = that.scaleX + "px";
219 | that.cropImage.style.top = that.scaleY + "px";
220 | };
221 |
222 | // 放开那个鼠标
223 | var end = function end(e) {
224 | // e.preventDefault();
225 | that.isDrag = false;
226 | if (that.isMobile) {
227 | document.removeEventListener('touchmove', move, false);
228 | document.removeEventListener('touchend', end, false);
229 | } else {
230 | document.removeEventListener('mousemove', move, false);
231 | document.removeEventListener('mouseup', end, false);
232 | }
233 | };
234 | if (that.isMobile) {
235 | if (e.touches.length == 2) {
236 | that.beginLength = that.beginLength == 0 ? that.touchData(e).length : that.beginLength;
237 | }
238 | document.addEventListener('touchmove', move);
239 | document.addEventListener('touchend', end);
240 | } else {
241 | document.addEventListener('mousemove', move);
242 | document.addEventListener('mouseup', end);
243 | }
244 | });
245 | _this2.hasEvent = true;
246 | }
247 | };
248 | this.img.src = url;
249 | }
250 | // 粘贴到canvas
251 |
252 | }, {
253 | key: "cropCanvas",
254 | value: function cropCanvas() {
255 | this.context.clearRect(0, 0, this.width, this.height);
256 | var sx = Math.round(Math.abs(this.scaleX) * this.img.naturalWidth / this.scaleWidth); //图像源x坐标
257 | var sy = Math.round(Math.abs(this.scaleY) * this.img.naturalHeight / this.scaleHeight); //图像源y坐标
258 | var sw = this.width * this.img.naturalWidth / this.scaleWidth >> 0;
259 | var sh = this.height * this.img.naturalHeight / this.scaleHeight >> 0;
260 | this.context.drawImage(this.img, sx, sy, sw, sh, 0, 0, this.width, this.height / this.cropRatio());
261 | }
262 |
263 | // http://stackoverflow.com/questions/11929099/html5-canvas-drawimage-ratio-bug-ios
264 | // 修正 iPhone 上传的方向问题
265 |
266 | }, {
267 | key: "cropRatio",
268 | value: function cropRatio() {
269 | var iw = this.img.naturalWidth,
270 | ih = this.img.naturalHeight;
271 | var canvas = document.createElement('canvas');
272 | canvas.width = 1;
273 | canvas.height = ih;
274 | var ctx = canvas.getContext('2d');
275 | ctx.drawImage(this.img, 0, 0);
276 | var data = ctx.getImageData(0, 0, 1, ih).data;
277 | // search image edge pixel position in case it is squashed vertically.
278 | var sy = 0;
279 | var ey = ih;
280 | var py = ih;
281 | while (py > sy) {
282 | var alpha = data[(py - 1) * 4 + 3];
283 | if (alpha === 0) {
284 | ey = py;
285 | } else {
286 | sy = py;
287 | }
288 | py = ey + sy >> 1;
289 | }
290 | var ratio = py / ih;
291 | return ratio === 0 ? 1 : ratio;
292 | }
293 | }, {
294 | key: "getOrientation",
295 | value: function getOrientation(file, callback) {
296 | var reader = new FileReader();
297 | reader.onload = function (e) {
298 |
299 | var view = new DataView(e.target.result);
300 | if (view.getUint16(0, false) != 0xFFD8) return callback(-2);
301 | var length = view.byteLength,
302 | offset = 2;
303 | while (offset < length) {
304 | var marker = view.getUint16(offset, false);
305 | offset += 2;
306 | if (marker == 0xFFE1) {
307 | if (view.getUint32(offset += 2, false) != 0x45786966) return callback(-1);
308 | var little = view.getUint16(offset += 6, false) == 0x4949;
309 | offset += view.getUint32(offset + 4, little);
310 | var tags = view.getUint16(offset, little);
311 | offset += 2;
312 | for (var i = 0; i < tags; i++) {
313 | if (view.getUint16(offset + i * 12, little) == 0x0112) return callback(view.getUint16(offset + i * 12 + 8, little));
314 | }
315 | } else if ((marker & 0xFF00) != 0xFF00) break;else offset += view.getUint16(offset, false);
316 | }
317 | return callback(-1);
318 | };
319 | reader.readAsArrayBuffer(file.slice(0, 64 * 1024));
320 | }
321 |
322 | //图片压缩
323 |
324 | }, {
325 | key: "fixedOrientation",
326 | value: function fixedOrientation(img, width, height, callback) {
327 | var canvas, ctx;
328 | canvas = document.createElement('canvas');
329 | canvas.width = width;
330 | canvas.height = height;
331 | ctx = canvas.getContext("2d");
332 | //如果图片方向等于6 ,则旋转矫正,反之则不做处理
333 | if (this.orientation == 6) {
334 | ctx.save();
335 | ctx.translate(width / 2, height / 2);
336 | ctx.rotate(90 * Math.PI / 180);
337 | ctx.drawImage(img, 0 - height / 2, 0 - width / 2, height, width);
338 | ctx.restore();
339 | } else {
340 | ctx.drawImage(img, 0, 0, width, height);
341 | }
342 | canvas.toBlob(function (blob) {
343 | var url = URL.createObjectURL(blob);
344 | callback && callback(url);
345 | });
346 | }
347 | // 放大缩小
348 |
349 | }, {
350 | key: "zoom",
351 | value: function zoom(e) {
352 | var s = parseInt(e.target.value) / 100;
353 | var osx = this.scaleX - this.width / 2;
354 | var osy = this.scaleY - this.height / 2;
355 | var os = void 0;
356 | // 按原始图片比例优先使用小值设置最低值
357 | if (this.temp.width > this.temp.height) {
358 | os = this.scaleHeight / this.height;
359 | this.scaleHeight = this.height * s;
360 | this.scaleWidth = this.scaleHeight * this.temp.width / this.temp.height;
361 | } else {
362 | os = this.scaleWidth / this.width;
363 | this.scaleWidth = this.width * s;
364 | this.scaleHeight = this.scaleWidth * this.temp.height / this.temp.width;
365 | }
366 | this.scaleX = osx * s / os + this.width / 2;
367 | this.scaleY = osy * s / os + this.height / 2;
368 | this.limit();
369 | this.cropCanvas();
370 | this.cropImage.style.width = this.scaleWidth + "px";
371 | this.cropImage.style.height = this.scaleHeight + "px";
372 | this.cropImage.style.left = this.scaleX + "px";
373 | this.cropImage.style.top = this.scaleY + "px";
374 | }
375 | // 限制拖动检测边缘
376 |
377 | }, {
378 | key: "limit",
379 | value: function limit() {
380 | if (this.scaleX < -(this.scaleWidth - this.width)) this.scaleX = -(this.scaleWidth - this.width);
381 | if (this.scaleY < -(this.scaleHeight - this.height)) this.scaleY = -(this.scaleHeight - this.height);
382 | if (this.scaleX > 0) this.scaleX = 0;
383 | if (this.scaleY > 0) this.scaleY = 0;
384 | if (this.scaleHeight === this.height) this.scaleY = 0;
385 | if (this.scaleWidth === this.width) this.scaleX = 0;
386 | }
387 |
388 | // 获取多点触控
389 |
390 | }, {
391 | key: "touchData",
392 | value: function touchData(e) {
393 | if (e.touches.length < 2) return;
394 | var x1 = e.touches[0].pageX;
395 | var x2 = e.touches[1].pageX;
396 | var x3 = x1 <= x2 ? (x2 - x1) / 2 + x1 : (x1 - x2) / 2 + x2;
397 | var y1 = e.touches[0].pageY - this.scrollbar.scrollTop;
398 | var y2 = e.touches[1].pageY - this.scrollbar.scrollTop;
399 | var y3 = y1 <= y2 ? (y2 - y1) / 2 + y1 : (y1 - y2) / 2 + y2;
400 | return {
401 | length: Math.round(Math.sqrt(Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2))),
402 | x: Math.round(x3),
403 | y: Math.round(y3)
404 | };
405 | }
406 | }, {
407 | key: "dataURLtoBlob",
408 | value: function dataURLtoBlob(dataurl) {
409 | var arr = dataurl.split(',');
410 | var mime = arr[0].match(/:(.*?);/)[1];
411 | var bstr = atob(arr[1]);
412 | var n = bstr.length,
413 | u8arr = new Uint8Array(n);
414 | while (n--) {
415 | u8arr[n] = bstr.charCodeAt(n);
416 | }
417 | return URL.createObjectURL(new Blob([u8arr], { type: mime }));
418 | }
419 | }]);
420 | return crop;
421 | }();
422 |
423 | return crop;
424 |
425 | })));
426 |
--------------------------------------------------------------------------------