一.效果
本案例中只对gif做了处理,预留了非gif的处理,可以自行处理。文章末尾会附上案例地址。
二.思路
相当于就是取裁剪框在图片中的位置和宽高,再根据帧数,取绘制区域大小及四个顶点的坐标绘制一遍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()">
<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 class="cut-upload-tip">
请上传50M以内的图片! 支持GIF、PNG、JPG、JPEG
<div class="priview-box">
<img :src="previewUrl" alt="" v-if="previewUrl">
<span v-else style="color: #666;">暂未裁剪图片!</span>
<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 slot="footer" class="dialog-footer">
<el-button @click="closeCut()">取 消</el-button>
<el-button type="primary" @click="finishCut()" :loading="cutLoading"
>{{cutLoading?'裁剪中...':'确定'}}</el-button
</el-dialog>
</template>
<script>
import $ from "jquery";
import Cropper from "cropperjs";
import "cropperjs/dist/cropper.css";
import GIF from 'gif.js'
import { GifToCanvas } from '@/libs/gifToCanvas.js'
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();
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 });
fileToBase64(file){
return new Promise((resolve)=>{
let reader = new FileReader();
reader.onload = function (evt) {
let base64 = evt.target.result;
resolve(base64)
reader.readAsDataURL(file);
async changeFile(e) {
let file = e.target.files[0];
if (file) {
this.fileinfo = file //保存file信息
this.imgType = file.type;
this.cutImgUrl = await this.fileToBase64(file);//file转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'){//gif处理
let blob = await this.cropGifHandle()
this.previewUrl = window.URL.createObjectURL(blob)
}else{//预留png
this.cutLoading = false
this.cropFlag = false
//gif裁剪
async cropGifHandle() {
return new Promise((resolve, reject) => {
if (this.myCropper) {
const url = URL.createObjectURL(this.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>
cropper-gif: vue裁剪gif图片,裁剪出来的图片仍然保留动画的案例,直接down下来就可运行,效果可见:https://root181.blog.csdn.net/article/details/117398384
Vue趣味【Vue3+Element Plus+Canvas实现一个简易画板;支持导出为图片】
Vue趣味【Vue3+Element Plus+Canvas实现一个简易画板;支持导出为图片】
vue img 图片需要自定义请求头
在做项目的时候,展示图片可能会用到nginx 的代理来进行展示,然而有些运维小哥哥喜欢展示技术,在展示图片的时候还需要前提传一个请求头,也就是账号和密码。