一.效果
本案例中只对gif做了处理,预留了非gif的处理,可以自行处理。文章末尾会附上案例地址。
这张图片有点大,请耐心等待2秒。
预览地址:点我就可以飞过去
二.思路
相当于就是取裁剪框在图片中的位置和宽高,再根据帧数,取绘制区域大小及四个顶点的坐标绘制一遍gif。
三.代码
<template>
<div id="app">
<div class="main cut">
<div class="cut-upload-wrap cut-model1">
<div class="cut-upload-container">
<div class="cut-upload-main">
<div class="cut-upload-btn" @click="uploadBtn()">
上传图片
</div>
<input
type="file"
style="opacity: 0;"
accept="image/gif,image/png,image/jpeg,image/jpg"
class="cut-upload-file com-input-avatar"
ref="J-uploadBtn"
id="J-uploadBtn"
@change="changeFile"
/>
</div>
<div class="cut-upload-tip">
请上传50M以内的图片! 支持GIF、PNG、JPG、JPEG
</div>
</div>
<div class="priview-box">
<img :src="previewUrl" alt="" v-if="previewUrl">
<span v-else style="color: #666;">暂未裁剪图片!</span>
</div>
</div>
</div>
<el-dialog
title="裁剪"
:visible.sync="cropFlag"
append-to-body
:destroy-on-close="true"
>
<div class="cropper-content">
<div class="cropper" style="text-align: center">
<img id="image" ref="cropper-img" :src="cutImgUrl" />
</div>
</div>
<div slot="footer" class="dialog-footer">
<el-button @click="closeCut()">取 消</el-button>
<el-button type="primary" @click="finishCut()" :loading="cutLoading"
>{{cutLoading?'裁剪中...':'确定'}}</el-button
>
</div>
</el-dialog>
</div>
</template>
<script>
import $ from "jquery";
import Cropper from "cropperjs";
import "cropperjs/dist/cropper.css";
import readFile from "js-file-reader";
import GIF from 'gif.js'
import { GifToCanvas } from '@/libs/gifToCanvas.js'
function dataURLtoBlob(dataurl) {
var arr = dataurl.split(','), mime = arr[0].match(/:(.*?);/)[1],
bstr = atob(arr[1]), n = bstr.length, u8arr = new Uint8Array(n);
while (n--) {
u8arr[n] = bstr.charCodeAt(n);
}
return new Blob([u8arr], { type: mime });
}
export default {
name: "App",
components: {},
data() {
return {
imgType: "image/gif",
cutImgUrl: "",
myCropper: "",
cropFlag: false,
cutLoading: false,
gifToCanvas:'',
gif:'',
previewUrl:'',
};
},
methods: {
uploadBtn() {
let uploadBtn = $("#J-uploadBtn");
uploadBtn.click();
},
async changeFile(e) {
let file = e.target.files[0];
if (file) {
console.log("file", file);
this.fileinfo = file //保存file信息
this.imgType = file.type;
let RESULT = await readFile(file);
console.log(RESULT);
this.cutImgUrl = RESULT[RESULT.length - 1].base64;
if (file.type == "image/gif") {
//gif图片
this.cropFlag = true;
this.$nextTick(() => {
setTimeout(() => {
this.myCropper = new Cropper(this.$refs["cropper-img"], {
aspectRatio: 300 / 300,
crop(event) {
console.log(event.detail.x);
console.log(event.detail.y);
console.log(event.detail.width);
console.log(event.detail.height);
console.log(event.detail.rotate);
console.log(event.detail.scaleX);
console.log(event.detail.scaleY);
},
});
}, 0);
});
} else {
//非gif图片
alert('请上传gif格式图片,本工程只处理gif图,但预留了非gif逻辑空间,有需要请自行补充!')
}
}
},
//关闭裁剪
closeCut(){
this.cropFlag = false;
this.cutLoading = false;
if(this.myCropper){
this.myCropper.destroy()
}
if(this.gif){
this.gif = ''
}
if(this.gifToCanvas){
this.gifToCanvas.clear()
}
},
async finishCut() {
if(this.cutLoading)return
this.cutLoading = true
if(this.imgType == 'image/gif'){
let blob = await this.cropGifHandle()
this.previewUrl = window.URL.createObjectURL(blob)
console.log('previewUrl',this.previewUrl)
}else{
}
this.cutLoading = false
this.cropFlag = false
},
//gif裁剪
async cropGifHandle() {
return new Promise((resolve, reject) => {
if (this.myCropper) {
const url = URL.createObjectURL(dataURLtoBlob(this.myCropper.url));
const cropBoxData = this.myCropper.getCropBoxData();
const canvasData = this.myCropper.getCanvasData();
this.gifToCanvas = new GifToCanvas(url, {
targetOffset: {
dx: cropBoxData.left - canvasData.left,
dy: cropBoxData.top - canvasData.top,
width: canvasData.width,
height: canvasData.height,
sWidth: cropBoxData.width,
sHeight: cropBoxData.height,
},
});
this.gif = new GIF({
workers: 4,
quality: 10,
width: cropBoxData.width,
height: cropBoxData.height,
workerScript: `${window.location.origin}/gif.worker.js`,
});
const addFrame = (canvas, delay) => {
this.gif.addFrame(canvas, { copy: true, delay });
};
this.gifToCanvas.on("progress", (canvas, delay) => {
addFrame(canvas, delay);
});
this.gifToCanvas.on("finished", (canvas, delay) => {
addFrame(canvas, delay);
this.gif.render();
});
this.gif.on("finished", (blob) => {
console.log("finished", window.URL.createObjectURL(blob));
resolve(blob);
});
this.gifToCanvas.init();
} else {
reject();
}
});
},
},
};
</script>
<style lang="less">
#app {
background: #000;
width: 100%;
height: 100%;
min-height: 100vh;
padding-top: 100px;
box-sizing: border-box;
.main {
width: 1200px;
margin: 0 auto;
box-sizing: border-box;
background: #1b1b1b;
&.cut {
min-height: 424px !important;
padding: 20px;
margin-bottom: 30px;
box-sizing: border-box;
.cut,
.cut-upload-main,
.cut-upload-wrap {
position: relative;
}
.cut {
height: 100%;
min-height: 424px !important;
padding: 20px;
margin-bottom: 30px;
}
.cut-model2 {
display: none;
}
.cut-upload-wrap {
text-align: center;
top: 50%;
margin-top: 100px;
display: flex;
justify-content: space-around;
}
.cut-upload-container {
display: inline-block;
padding: 35px;
border: 5px dashed #262626;
}
.cut-info,
.cut-upload-tip {
padding-top: 20px;
font-size: 14px;
}
.cut-upload-btn {
width: 385px;
height: 60px;
line-height: 60px;
color: #fff;
background: #6418ff;
-webkit-transition: 0.2s;
-o-transition: 0.2s;
transition: 0.2s;
cursor: pointer;
}
.cut-upload-main:hover .cut-upload-btn {
background: #5e12fb;
}
.cut-upload-file {
position: absolute;
}
.cut-upload-tip {
color: #666;
}
}
}
}
</style>
四.案例地址
https://gitee.com/huqinggui/cropper-gif.git
原文地址:https://blog.csdn.net/qq_32442973/article/details/117398384