上传图片之前,按照一定比例进行剪裁,剪裁后上传到服务器
官方地址

安装

  1. npm install vue-cropper
  1. <template>
  2. <div class="show-info">
  3. <div class="test test1">
  4. <vueCropper
  5. ref="cropper"
  6. :img="option.img"
  7. :outputSize="option.size"
  8. :outputType="option.outputType"
  9. :info="true"
  10. :full="option.full"
  11. :canMove="option.canMove"
  12. :canMoveBox="option.canMoveBox"
  13. :fixedBox="option.fixedBox"
  14. :original="option.original"
  15. :autoCrop="option.autoCrop"
  16. :autoCropWidth="option.autoCropWidth"
  17. :autoCropHeight="option.autoCropHeight"
  18. :centerBox="option.centerBox"
  19. :high="option.high"
  20. :infoTrue="option.infoTrue"
  21. @realTime="realTime"
  22. @imgLoad="imgLoad"
  23. @cropMoving="cropMoving"
  24. :enlarge="option.enlarge"
  25. ></vueCropper>
  26. </div>
  27. <div class="test-button">
  28. <button @click="changeImg" class="btn">随机图片</button>
  29. <label class="btn" for="uploads">上传图片</label>
  30. <input
  31. type="file"
  32. id="uploads"
  33. style="position: absolute; clip: rect(0 0 0 0)"
  34. accept="image/png, image/jpeg, image/gif, image/jpg"
  35. @change="uploadImg($event, 1)"
  36. />
  37. <button @click="startCrop" v-if="!crap" class="btn">开始裁剪</button>
  38. <button @click="stopCrop" v-else class="btn">停止裁剪</button>
  39. <button @click="clearCrop" class="btn">清除</button>
  40. <button @click="refreshCrop" class="btn">刷新</button>
  41. <button @click="changeScale(1)" class="btn">放大</button>
  42. <button @click="changeScale(-1)" class="btn">缩小</button>
  43. <button @click="rotateLeft" class="btn">左旋转</button>
  44. <button @click="rotateRight" class="btn">右旋转</button>
  45. <button @click="finish('base64')" class="btn">生成图片base64</button>
  46. <button @click="finish('blob')" class="btn">生成图片blob</button>
  47. <a @click="down('base64')" class="btn">base64下载</a>
  48. <a @click="down('blob')" class="btn">blob下载</a>
  49. <a :href="downImg" download="demo.png" ref="downloadDom">{{ downImg }}</a>
  50. </div>
  51. <!-- -->
  52. <div class="box">
  53. <div class="input">
  54. <h3>输出图片</h3>
  55. <div
  56. class="show-preview"
  57. :style="{ width: previews.w + 'px', height: previews.h + 'px', overflow: 'hidden', margin: '5px' }"
  58. >
  59. <div :style="previews.div">
  60. <img :src="previews.url" :style="previews.img" />
  61. </div>
  62. </div>
  63. </div>
  64. <el-row >
  65. <el-col :span="12" class="c-item">
  66. <span>上传图片是否显示原始宽高 (针对大图 可以铺满)</span>
  67. <input type="checkbox" v-model="option.original" />
  68. <span>original: {{ option.original }}</span>
  69. </el-col>
  70. <el-col :span="12" class="c-item">
  71. <span>是否根据dpr生成适合屏幕的高清图片</span>
  72. <input type="checkbox" v-model="option.high" />
  73. <span>high: {{ option.high }}</span>
  74. </el-col>
  75. <el-col :span="12" class="c-item">
  76. <span>是否输出原图比例的截图</span>
  77. <input type="checkbox" v-model="option.full" />
  78. <span>full: {{ option.full }}</span>
  79. </el-col>
  80. <el-col :span="12" class="c-item">
  81. <span>截图信息展示是否是真实的输出宽高</span>
  82. <input type="checkbox" v-model="option.infoTrue" />
  83. <span>infoTrue: {{ option.infoTrue }}</span>
  84. </el-col>
  85. <el-col :span="12" class="c-item">
  86. <span>能否拖动图片</span>
  87. <input type="checkbox" v-model="option.canMove" />
  88. <span>canMove: {{ option.canMove }}</span>
  89. </el-col>
  90. <el-col :span="12" class="c-item">
  91. <span>能否拖动截图框</span>
  92. <input type="checkbox" v-model="option.canMoveBox" />
  93. <span>canMoveBox: {{ option.canMoveBox }}</span>
  94. </el-col>
  95. <el-col :span="12" class="c-item">
  96. <span>截图框固定大小</span>
  97. <input type="checkbox" v-model="option.fixedBox" />
  98. <span>fixedBox: {{ option.fixedBox }}</span>
  99. </el-col>
  100. <el-col :span="12" class="c-item">
  101. <span>是否自动生成截图框</span>
  102. <input type="checkbox" v-model="option.autoCrop" />
  103. <span>autoCrop: {{ option.autoCrop }}</span>
  104. </el-col>
  105. <el-col :span="12" class="c-item">
  106. <span>截图框是否限制在图片里(只有在自动生成截图框时才能生效)</span>
  107. <input type="checkbox" v-model="option.centerBox" />
  108. <span>centerBox: {{ option.centerBox }}</span>
  109. </el-col>
  110. <el-col :span="12" class="c-item">
  111. <span>是否按照截图框比例输出 默认为1 </span>
  112. <input type="number" v-model="option.enlarge" />
  113. </el-col>
  114. <el-col :span="12" class="c-item">
  115. <span>输出图片格式 </span>
  116. <label><input type="radio" name="type" value="jpeg" v-model="option.outputType" />jpeg </label>
  117. <label><input type="radio" name="type" value="jpg" v-model="option.outputType" />jpg </label>
  118. <label> <input type="radio" name="type" value="png" v-model="option.outputType" />png</label>
  119. <label> <input type="radio" name="type" value="webp" v-model="option.outputType" />webp</label>
  120. </el-col>
  121. </el-row>
  122. </div>
  123. <div slot="body">{{ code1 }}</div>
  124. </div>
  125. </template>
  126. <script>
  127. import { VueCropper } from 'vue-cropper'
  128. export default {
  129. components: {
  130. VueCropper,
  131. },
  132. data() {
  133. return {
  134. // 初始化值
  135. option: {
  136. size: 1,
  137. full: true, // 是否输出原图比例的截图
  138. canMove: true, // 上传图片是否可以移动
  139. fixedBox: false, // 固定截图框大小 不允许改变
  140. original: false, // 上传图片按照原始比例渲染
  141. canMoveBox: true, // 截图框能否拖动
  142. img: 'https://avatars3.githubusercontent.com/u/15681693', // url 地址 || base64 || blob null
  143. outputSize: 1, // 裁剪生成图片的质量 1 (0-1)
  144. outputType: 'jpg', // 裁剪生成图片的格式 jpg (jpg 需要传入jpeg)
  145. info: '', // 裁剪框的大小信息 true
  146. canScale: true, // 图片是否允许滚轮缩放 true
  147. autoCrop: true, // 是否默认生成截图框 false
  148. fixed: true, // 是否开启截图框宽高固定比例 true
  149. // 只有自动截图开启 宽度高度才生效
  150. autoCropWidth: 200, // 默认生成截图框宽度
  151. autoCropHeight: 150, // 默认生成截图框高度
  152. centerBox: true, // 截图框是否被限制在图片里面
  153. high: true, // 是否按照设备的dpr 输出等比例图片
  154. infoTrue: false, // true 为展示真实输出图片宽高 false 展示看到的截图框宽高
  155. w: 200,
  156. h: 200,
  157. maxImgSize: 1200, // 限制图片最大宽度和高度
  158. // enlarge:1, //图片根据截图框输出比例倍数
  159. // fixedNumber:[1,1] // 截图框的宽高比例
  160. // mode: 'contain',
  161. },
  162. // 截图后值
  163. previews: {},
  164. crap: false,
  165. previewStyle1: {},
  166. previewStyle2: {},
  167. code1: '',
  168. lists: [
  169. {
  170. img: 'https://img2.baidu.com/it/u=2102736929,2417598652&fm=26&fmt=auto&gp=0.jpg',
  171. },
  172. {
  173. img: 'https://img0.baidu.com/it/u=1330784235,4146572500&fm=26&fmt=auto&gp=0.jpg',
  174. },
  175. {
  176. img: 'https://img0.baidu.com/it/u=4015235454,569502415&fm=26&fmt=auto&gp=0.jpg',
  177. },
  178. {
  179. img: 'https://img2.baidu.com/it/u=3566088443,3713209594&fm=26&fmt=auto&gp=0.jpg',
  180. },
  181. {
  182. img: 'https://img0.baidu.com/it/u=3870964477,3746012709&fm=26&fmt=auto&gp=0.jpg',
  183. },
  184. {
  185. img: 'https://img2.baidu.com/it/u=360400461,2955275651&fm=26&fmt=auto&gp=0.jpg',
  186. },
  187. ],
  188. index: 0,
  189. downImg: '',
  190. }
  191. },
  192. mounted() {
  193. console.log(window['vue-cropper'])
  194. },
  195. methods: {
  196. // 上传图片
  197. uploadImg(val, num) {
  198. console.log(val, num)
  199. const file = val.target.files[0]
  200. if (!/\.(gif|jpg|jpeg|png|bmp|GIF|JPG|PNG)$/.test(val.target.value)) {
  201. console.log('图片类型必须是.gif,jpeg,jpg,png,bmp中的一种')
  202. return false
  203. }
  204. const reader = new FileReader()
  205. reader.onload = e => {
  206. let data
  207. if (typeof e.target.result === 'object') {
  208. // 把Array Buffer转化为blob 如果是base64不需要
  209. data = window.URL.createObjectURL(new Blob([e.target.result]))
  210. } else {
  211. data = e.target.result
  212. }
  213. if (num === 1) {
  214. this.option.img = data
  215. } else if (num === 2) {
  216. this.example2.img = data
  217. }
  218. this.previews = this.option
  219. console.log(this.option.img)
  220. }
  221. // 转化为base64
  222. // reader.readAsDataURL(file)
  223. // 转化为blob
  224. reader.readAsArrayBuffer(file)
  225. },
  226. imgLoad(val) {
  227. console.log(val)
  228. },
  229. // 截图框移动回调函数
  230. cropMoving(val) {
  231. console.log(val)
  232. },
  233. changeImg(val) {
  234. const num = Math.ceil(Math.random() * this.lists.length)
  235. console.log(val, num, this.lists[num].img)
  236. this.option.img = this.lists[num].img
  237. },
  238. // 开始截图
  239. startCrop() {
  240. this.crap = true
  241. this.$refs.cropper.startCrop()
  242. },
  243. // 停止截图
  244. stopCrop() {
  245. this.crap = false
  246. this.$refs.cropper.stopCrop()
  247. },
  248. // 清除截图
  249. clearCrop() {
  250. this.$refs.cropper.clearCrop()
  251. },
  252. // 刷新
  253. refreshCrop(val) {
  254. console.log(val)
  255. this.$refs.cropper.refresh()
  256. },
  257. // 修改图片大小 正数为变大 负数变小
  258. changeScale(val) {
  259. console.log(val)
  260. const num = val || 1
  261. this.$refs.cropper.changeScale(num)
  262. },
  263. // 向右边旋转90度
  264. rotateLeft(val) {
  265. console.log(val)
  266. this.$refs.cropper.rotateRight()
  267. },
  268. // 向左边旋转90度
  269. rotateRight(val) {
  270. console.log(val)
  271. this.$refs.cropper.rotateLeft()
  272. },
  273. // 获取截图信息
  274. getInfo() {
  275. const W = this.$refs.cropper.cropW // 截图框宽度
  276. const H = this.$refs.cropper.cropH // 截图框高度
  277. console.log(W, H)
  278. },
  279. // down 下载
  280. down(val) {
  281. console.log(val)
  282. const aLink = document.createElement('a')
  283. aLink.download = 'demo'
  284. // 输出
  285. if (val === 'blob') {
  286. // 获取截图的blob数据
  287. this.$refs.cropper.getCropBlob(data => {
  288. this.downImg = window.URL.createObjectURL(data)
  289. aLink.href = window.URL.createObjectURL(data)
  290. aLink.click()
  291. })
  292. } else {
  293. // 获取截图的base64 数据
  294. this.$refs.cropper.getCropData(data => {
  295. this.downImg = data
  296. aLink.href = data
  297. aLink.click()
  298. })
  299. }
  300. },
  301. // 输出
  302. finish(val) {
  303. console.log(val)
  304. // const test = window.open('about:blank')
  305. // test.document.body.innerHTML = '图片生成中..'
  306. if (val === 'blob') {
  307. this.$refs.cropper.getCropBlob(data => {
  308. const img = window.URL.createObjectURL(data)
  309. this.model = true
  310. this.modelSrc = img
  311. })
  312. } else {
  313. this.$refs.cropper.getCropData(data => {
  314. this.model = true
  315. this.modelSrc = data
  316. })
  317. }
  318. },
  319. // 添加预览
  320. realTime(data) {
  321. console.log(data)
  322. this.previews = data
  323. },
  324. // 获取图片基于容器的坐标点
  325. getImgAxis(val) {
  326. console.log(val)
  327. this.$refs.cropper.getImgAxis()
  328. },
  329. // 获取截图框基于容器的坐标点
  330. getCropAxis(val) {
  331. console.log(val)
  332. this.$refs.cropper.getCropAxis()
  333. },
  334. // 自动生成截图框函数
  335. goAutoCrop(val) {
  336. const go = this.$refs.cropper.goAutoCrop
  337. console.log(val, go)
  338. },
  339. finish2(type) {
  340. console.log(type)
  341. this.$refs.cropper2.getCropData(data => {
  342. this.model = true
  343. this.modelSrc = data
  344. })
  345. },
  346. finish3(type) {
  347. console.log(type)
  348. this.$refs.cropper3.getCropData(data => {
  349. this.model = true
  350. this.modelSrc = data
  351. })
  352. },
  353. },
  354. }
  355. </script>
  356. <style lang="scss" scoped>
  357. .box {
  358. display: flex;
  359. }
  360. .cut {
  361. width: 500px;
  362. height: 500px;
  363. margin: 30px auto;
  364. }
  365. .c-item {
  366. max-width: 500px;
  367. margin: 10px auto;
  368. margin-top: 12px;
  369. }
  370. .content {
  371. margin: auto;
  372. max-width: 1200px;
  373. margin-bottom: 100px;
  374. }
  375. .test-button {
  376. display: flex;
  377. flex-wrap: wrap;
  378. align-content: center;
  379. justify-content: center;
  380. }
  381. .set{
  382. display: flex;
  383. }
  384. .btn {
  385. display: inline-block;
  386. line-height: 1;
  387. white-space: nowrap;
  388. cursor: pointer;
  389. background: #fff;
  390. border: 1px solid #c0ccda;
  391. color: #1f2d3d;
  392. text-align: center;
  393. box-sizing: border-box;
  394. outline: none;
  395. margin: 20px 10px 0px 0px;
  396. padding: 9px 15px;
  397. font-size: 14px;
  398. border-radius: 4px;
  399. color: #fff;
  400. background-color: #50bfff;
  401. border-color: #50bfff;
  402. transition: all 0.2s ease;
  403. text-decoration: none;
  404. user-select: none;
  405. }
  406. .des {
  407. line-height: 30px;
  408. }
  409. code.language-html {
  410. padding: 10px 20px;
  411. margin: 10px 0px;
  412. display: block;
  413. background-color: #333;
  414. color: #fff;
  415. overflow-x: auto;
  416. font-family: Consolas, Monaco, Droid, Sans, Mono, Source, Code, Pro, Menlo, Lucida, Sans, Type, Writer, Ubuntu, Mono;
  417. border-radius: 5px;
  418. white-space: pre;
  419. }
  420. .show-info {
  421. margin-bottom: 10px;
  422. margin-top: 10px;
  423. }
  424. .show-info h2 {
  425. line-height: 50px;
  426. }
  427. /*.title, .title:hover, .title-focus, .title:visited {
  428. color: black;
  429. }*/
  430. .title {
  431. display: block;
  432. text-decoration: none;
  433. text-align: center;
  434. line-height: 1.5;
  435. margin: 20px 0px;
  436. background-image: -webkit-linear-gradient(
  437. left,
  438. #3498db,
  439. #f47920 10%,
  440. #d71345 20%,
  441. #f7acbc 30%,
  442. #ffd400 40%,
  443. #3498db 50%,
  444. #f47920 60%,
  445. #d71345 70%,
  446. #f7acbc 80%,
  447. #ffd400 90%,
  448. #3498db
  449. );
  450. color: transparent;
  451. -webkit-background-clip: text;
  452. background-size: 200% 100%;
  453. animation: slide 5s infinite linear;
  454. font-size: 40px;
  455. }
  456. .test {
  457. height: 500px;
  458. }
  459. .model {
  460. position: fixed;
  461. z-index: 10;
  462. width: 100vw;
  463. height: 100vh;
  464. overflow: auto;
  465. top: 0;
  466. left: 0;
  467. background: rgba(0, 0, 0, 0.8);
  468. }
  469. .model-show {
  470. display: flex;
  471. justify-content: center;
  472. align-items: center;
  473. width: 100vw;
  474. height: 100vh;
  475. }
  476. .model img {
  477. display: block;
  478. margin: auto;
  479. max-width: 80%;
  480. user-select: none;
  481. background-position: 0px 0px, 10px 10px;
  482. background-size: 20px 20px;
  483. background-image: linear-gradient(45deg, #eee 25%, transparent 25%, transparent 75%, #eee 75%, #eee 100%),
  484. linear-gradient(45deg, #eee 25%, white 25%, white 75%, #eee 75%, #eee 100%);
  485. }
  486. .c-item {
  487. display: block;
  488. user-select: none;
  489. }
  490. @keyframes slide {
  491. 0% {
  492. background-position: 0 0;
  493. }
  494. 100% {
  495. background-position: -100% 0;
  496. }
  497. }
  498. </style>