1 |
|
2 | <template>
|
3 | <div class="crop-bg" v-show="show" style="z-index:20000">
|
4 | <div class="crop-box" :style="boxStyle">
|
5 | <div class="crop-title">图片裁剪</div>
|
6 | <div class="close" @click="close">X</div>
|
7 | <div class="crop-choose">
|
8 | <label for="crop-input">
|
9 | <input accept=".jpg, .jpeg, .png, .gif" id="crop-input" ref="input" type="file" @change="change" capture>
|
10 | <span class="crop-btn">选择文件</span>
|
11 | </label>
|
12 | <button v-show="ready" class="crop-btn crop-btn-crop" @click="crop">裁切图片</button>
|
13 | </div>
|
14 | <div class="crop-content" :style="contentStyle">
|
15 | <img :src="src" @load="start" ref="img" class="thumb">
|
16 | </div>
|
17 | </div>
|
18 | </div>
|
19 | </template>
|
20 | <script>
|
21 | /**
|
22 | *<yn-crop :visible.sync="shwoCrop" @crop="onCrop" :layout="[700,500]" :size="[160,160]" />
|
23 | */
|
24 | import Cropper from "cropperjs";
|
25 | import "cropperjs/dist/cropper.css";
|
26 | export default {
|
27 | props: {
|
28 | visible: {
|
29 | type: Boolean,
|
30 | default: false
|
31 | },
|
32 | layout: {
|
33 | type: Array,
|
34 | default: () => [800, 600]
|
35 | },
|
36 | size: {
|
37 | type: Array,
|
38 | default: () => [200, 200]
|
39 | }
|
40 | },
|
41 | data() {
|
42 | return {
|
43 | show: false,
|
44 | src: "",
|
45 | cropper: null,
|
46 | ready: false
|
47 | };
|
48 | },
|
49 |
|
50 | watch: {
|
51 | visible(val) {
|
52 | this.show = val;
|
53 | }
|
54 | },
|
55 | computed: {
|
56 | ratio() {
|
57 | return this.size[0] / this.size[1];
|
58 | },
|
59 | boxStyle() {
|
60 | return {
|
61 | width: this.layout[0] + "px",
|
62 | height: this.layout[1] + "px"
|
63 | };
|
64 | },
|
65 | contentStyle() {
|
66 | return {
|
67 | height: this.layout[1] - 40 + "px"
|
68 | };
|
69 | },
|
70 | previewStyle() {
|
71 | const width = this.layout[0] * 0.3 * 0.8;
|
72 | return {
|
73 | width: width + "px",
|
74 | height: width * this.ratio + "px"
|
75 | };
|
76 | }
|
77 | },
|
78 | methods: {
|
79 | prevent(e) {
|
80 | e.stopPropagation();
|
81 | },
|
82 | start() {
|
83 | const self = this;
|
84 | this.stop();
|
85 | this.cropper = new Cropper(this.$refs.img, {
|
86 | aspectRatio: this.ratio,
|
87 | minCropBoxWidth: this.size[0],
|
88 | minCropBoxHeight: this.size[1],
|
89 | ready: () => (self.ready = true)
|
90 | });
|
91 | },
|
92 | read(files) {
|
93 | return new Promise((resolve, reject) => {
|
94 | if (!files || files.length === 0) {
|
95 | return resolve();
|
96 | }
|
97 | const file = files[0];
|
98 | if (/^image\/\w+$/.test(file.type)) {
|
99 | const reader = new FileReader();
|
100 | reader.onload = () => {
|
101 | this.src = reader.result;
|
102 | resolve();
|
103 | };
|
104 | reader.onerror = reject;
|
105 | reader.onabort = reject;
|
106 | reader.readAsDataURL(file);
|
107 | } else {
|
108 | reject("Please choose an image file.");
|
109 | }
|
110 | });
|
111 | },
|
112 |
|
113 | crop() {
|
114 | const canvas = this.cropper.getCroppedCanvas({
|
115 | width: this.size[0],
|
116 | height: this.size[1]
|
117 | });
|
118 | const base64 = canvas.toDataURL();
|
119 | this.$emit("crop", base64);
|
120 | this.close();
|
121 | },
|
122 | change({ target }) {
|
123 | this.read(target.files)
|
124 | .then(() => (target.value = ""))
|
125 | .catch(e => {
|
126 | target.value = "";
|
127 | this.alert(e);
|
128 | });
|
129 | },
|
130 | alert(e) {
|
131 | window.alert(e && e.message ? e.message : e);
|
132 | },
|
133 | close() {
|
134 | this.src = "";
|
135 | this.stop();
|
136 | this.$emit("update:visible", false);
|
137 | },
|
138 | stop() {
|
139 | if (this.cropper) {
|
140 | this.cropper.destroy();
|
141 | this.cropper = null;
|
142 | this.ready = false;
|
143 | }
|
144 | }
|
145 | }
|
146 | };
|
147 | </script>
|
148 | <style scoped lang="scss">
|
149 | .crop-bg {
|
150 | position: fixed;
|
151 | width: 100%;
|
152 | height: 100%;
|
153 | left: 0;
|
154 | top: 0;
|
155 | background: rgba(0, 0, 0, 0.281);
|
156 | user-select: none;
|
157 | z-index: 2000;
|
158 | }
|
159 | .crop-box {
|
160 | position: absolute;
|
161 | top: 0;
|
162 | bottom: 0;
|
163 | left: 0;
|
164 | right: 0;
|
165 | margin: auto;
|
166 | background: rgb(255, 255, 255);
|
167 | width: 600px;
|
168 | height: 400px;
|
169 | }
|
170 | .crop-title {
|
171 | height: 50px;
|
172 | line-height: 50px;
|
173 | text-indent: 20px;
|
174 | background: rgb(245, 245, 245);
|
175 | position: relative;
|
176 | border-bottom: 1px solid #d4d4d4;
|
177 | }
|
178 | .crop-content {
|
179 | overflow: hidden;
|
180 | background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAAA3NCSVQICAjb4U/gAAAABlBMVEXMzMz////TjRV2AAAACXBIWXMAAArrAAAK6wGCiw1aAAAAHHRFWHRTb2Z0d2FyZQBBZG9iZSBGaXJld29ya3MgQ1M26LyyjAAAABFJREFUCJlj+M/AgBVhF/0PAH6/D/HkDxOGAAAAAElFTkSuQmCC");
|
181 | }
|
182 |
|
183 | .crop-choose {
|
184 | overflow: hidden;
|
185 | position: absolute;
|
186 | top: 9px;
|
187 | left: 100px;
|
188 | }
|
189 | #crop-input {
|
190 | width: 1px;
|
191 | position: relative;
|
192 | left: -1px;
|
193 | opacity: 0.01;
|
194 | }
|
195 |
|
196 | .crop-btn {
|
197 | font-size: 13px;
|
198 | padding: 6px 15px;
|
199 | background: rgb(0, 60, 255);
|
200 | border-radius: 2px;
|
201 | color: white;
|
202 | display: inline-block;
|
203 | cursor: pointer;
|
204 | border-radius: 2px;
|
205 | }
|
206 | .crop-btn-crop {
|
207 | background: red;
|
208 | }
|
209 | .thumb {
|
210 | max-width: 100%;
|
211 | max-height: 100%;
|
212 | }
|
213 | .close {
|
214 | display: inline-block;
|
215 | position: absolute;
|
216 | right: 10px;
|
217 | top: 10px;
|
218 | cursor: pointer;
|
219 | }
|
220 | </style>
|