当前位置 博文首页 > 纸飞机博客:vue裁剪gif图片并保持动画效果

    纸飞机博客:vue裁剪gif图片并保持动画效果

    作者:[db:作者] 时间:2021-08-16 16:00

    一.效果

    本案例中只对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以内的图片!&nbsp;&nbsp;支持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

    cs