精华内容
下载资源
问答
  • 主要为大家详细介绍了vue实现移动端图片裁剪上传功能,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
  • 小程序图片裁剪上传

    2019-01-28 20:25:21
    小程序图片裁剪上传, 封装的组件,可调整裁剪框大小, 可多张图片上传
  • 图片裁剪上传

    2018-11-19 13:38:18
    使用cropper.js实现图片裁剪,简单易懂,上传方法已写好,开封即用,只需要调整自己需要的样式及后台的接口即可
  • PHP支持手势的手机端图片裁剪上传 PHP支持手势的手机端图片裁剪上传
  • php+html+jquery实现图片裁剪上传
  • javascript asp.net 图片剪切上传 图片裁剪 上传保存,无刷新剪切图片,并把剪切后的图片保存到服务器。
  • 图片裁剪上传.zip

    2019-09-09 11:45:53
    利用Jcrop.js裁剪图片上传到后台执行其他操作
  • photoclip进行图片裁剪上传,里面有demo页面供大家参考
  • 主要介绍了jquery.Jcrop结合JAVA后台实现图片裁剪上传实例,具有一定的参考价值,感兴趣的小伙伴们可以参考一下。
  • Angular 图片裁剪上传插件

    千次阅读 2017-08-23 18:36:04
    本文将介绍基于Angular的图片裁剪上传插件。 github: https://github.com/licaomeng/angular-image-upload 插件效果如下: 该插件的图片裁剪是通过图片的放大、缩小、拖动完成的。而不同于我们通常所见到的拖动...

    本文将介绍基于Angular的图片裁剪上传插件。
    github: https://github.com/licaomeng/angular-image-upload
    希望大家帮忙Star一下我的项目,我会持续更新~O(∩_∩)O

    插件效果如下:
    这里写图片描述

    该插件的图片裁剪是通过图片的放大、缩小、拖动完成的。而不同于我们通常所见到的拖动剪裁范围,进行的图片剪裁。这是一种反向思维。

    imgZoomCanvas.js

    图片的放大、缩小、拖动,全部是在html5的Canvas上面完成的。实现该算法的核心代码封装在 imgZoomCanvas.js 里面。

    /**
     * Created by Caomeng Li on 8/23/2016.
     */
    angular.module('appModule')
        .factory('imgZoomCanvas', [function () {
    
            //singleton
            var INSTANCE = null;
    
            var getInstance = function (options) {
                return INSTANCE || ( INSTANCE = new CanvasZoom(options) );
            }
    
            var destroyInstance = function () {
                if (INSTANCE) {
                    INSTANCE = null;
                }
            }
    
            var stopAnimate = function () {
                return INSTANCE ? INSTANCE.stopAnimate() : null;
            }
    
            var onZoom = function (zoom) {
                return INSTANCE ? INSTANCE.doZoom(zoom) : null;
            }
    
            var CanvasZoom = function (options) {
                if (!options || !options.canvas) {
                    throw 'CanvasZoom constructor: missing arguments canvas';
                }
                if (!options.image) {
                    throw 'CanvasZoom constructor: missing arguments image';
                }
    
                this.canvas = options.canvas;
                this.image = options.image;
                this.currentAnimationId = 0;
                this.canvas.width = this.canvas.clientWidth;
                this.canvas.height = this.canvas.clientHeight;
                this.context = this.canvas.getContext('2d');
    
                this.lastX = 0;
                this.lastY = 0;
    
                this.position = {
                    x: 0,
                    y: 0
                };
    
                this.initPosition = {
                    x: 0,
                    y: 0
                }
    
                this.scale = {
                    x: 1,
                    y: 1
                };
    
                this.initScale = {
                    x: 1,
                    y: 1
                };
    
                this.init = false;
    
                this.checkRequestAnimationFrame();
                this.currentAnimationId = requestAnimationFrame(this.animate.bind(this));
    
                this.setEventListeners();
            }
    
            CanvasZoom.prototype = {
                stopAnimate: function () {
                    cancelAnimationFrame(this.currentAnimationId);
                },
    
                animate: function () {
                    this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
                    var imgWidth = this.image.width,
                        imgHeight = this.image.height;
                    if (!this.init) {
                        if (imgWidth > imgHeight) {
                            this.scale.x = this.scale.y = this.canvas.height / imgHeight;
                        } else {
                            this.scale.x = this.scale.y = this.canvas.width / imgWidth;
                        }
                        this.initScale.x = this.scale.x;
                        this.initScale.y = this.scale.y;
                    }
                    var currentWidth = (this.image.width * this.scale.x);
                    var currentHeight = (this.image.height * this.scale.y);
    
                    if (!this.init) {
                        if (imgWidth > imgHeight) {
                            this.position.x = (currentWidth - this.canvas.width) / 2;
                            this.position.x = this.position.x > 0 ? -this.position.x : this.position.x;
                        } else {
                            this.position.y = (currentHeight - this.canvas.height) / 2;
                            this.position.y = this.position.y > 0 ? -this.position.y : this.position.y;
                        }
                        this.initPosition.x = this.position.x;
                        this.initPosition.y = this.position.y
                        this.init = true;
                    }
    
                    this.context.drawImage(this.image, this.position.x, this.position.y, currentWidth, currentHeight);
                    this.currentAnimationId = requestAnimationFrame(this.animate.bind(this));
                },
    
                doZoom: function (zoom) {
                    if (!zoom) return;
    
                    //new scale
                    var currentScale = this.scale.x;
                    var newScale = this.scale.x + zoom * this.scale.x / 100;
    
                    //some helpers
                    var deltaScale = newScale - currentScale;
                    var currentWidth = (this.image.width * this.scale.x);
                    var currentHeight = (this.image.height * this.scale.y);
                    var deltaWidth = this.image.width * deltaScale;
                    var deltaHeight = this.image.height * deltaScale;
    
                    //by default scale doesnt change position and only add/remove pixel to right and bottom
                    //so we must move the image to the left to keep the image centered
                    //ex: coefX and coefY = 0.5 when image is centered <=> move image to the left 0.5x pixels added to the right
                    var canvasmiddleX = this.canvas.clientWidth / 2;
                    var canvasmiddleY = this.canvas.clientHeight / 2;
                    var xonmap = (-this.position.x) + canvasmiddleX;
                    var yonmap = (-this.position.y) + canvasmiddleY;
                    var coefX = -xonmap / (currentWidth);
                    var coefY = -yonmap / (currentHeight);
                    var newPosX = this.position.x + deltaWidth * coefX;
                    var newPosY = this.position.y + deltaHeight * coefY;
    
                    //edges cases
                    var newWidth = currentWidth + deltaWidth;
                    var newHeight = currentHeight + deltaHeight;
    
                    if (newPosX > 0) {
                        newPosX = 0;
                    }
                    if (newPosX + newWidth < this.canvas.clientWidth) {
                        newPosX = this.canvas.clientWidth - newWidth;
                    }
    
                    if (newHeight < this.canvas.clientHeight) return;
                    if (newPosY > 0) {
                        newPosY = 0;
                    }
                    if (newPosY + newHeight < this.canvas.clientHeight) {
                        newPosY = this.canvas.clientHeight - newHeight;
                    }
    
                    //finally affectations
                    this.scale.x = newScale;
                    this.scale.y = newScale;
                    this.position.x = newPosX;
                    this.position.y = newPosY;
    
                    //edge cases
                    if (this.scale.x < this.initScale.x) {
                        this.scale.x = this.initScale.x;
                        this.scale.y = this.initScale.x;
                        this.position.x = this.initPosition.x;
                        this.position.y = this.initPosition.y;
                    }
                },
    
                doMove: function (relativeX, relativeY) {
                    if (this.lastX && this.lastY) {
    
                        console.log('relativeX', relativeX);
                        console.log('relativeY', relativeY);
    
                        console.log('this.lastX', this.lastX);
                        console.log('this.lastY', this.lastY);
    
                        var deltaX = relativeX - this.lastX;
                        var deltaY = relativeY - this.lastY;
                        console.log('deltaX', deltaX);
                        console.log('deltaY', deltaY);
    
                        var currentWidth = (this.image.width * this.scale.x);
                        var currentHeight = (this.image.height * this.scale.y);
    
                        this.position.x += deltaX;
                        this.position.y += deltaY;
                        console.log('this.position.x', this.position.x);
                        console.log('this.position.y', this.position.y);
    
                        // edge cases
                        if (this.position.x >= 0) {
                            this.position.x = 0;
                        } else if (this.position.x < 0 && this.position.x + currentWidth < this.canvas.width) {
                            this.position.x = this.canvas.width - Math.round(currentWidth);
                        }
    
                        if (this.position.y >= 0) {
                            this.position.y = 0;
                        } else if (this.position.y < 0 && this.position.y + currentHeight < this.canvas.height) {
                            this.position.y = this.canvas.height - Math.round(currentHeight);
                        }
                    }
                    this.lastX = relativeX;
                    this.lastY = relativeY;
                },
    
                setEventListeners: function () {
                    this.canvas.addEventListener('mousedown', function (e) {
                        this.mdown = true;
                        this.lastX = 0;
                        this.lastY = 0;
                    }.bind(this));
    
                    this.canvas.addEventListener('mouseup', function (e) {
                        this.mdown = false;
                    }.bind(this));
    
                    this.canvas.addEventListener('mousemove', function (e) {
                        var relativeX = e.pageX - this.canvas.getBoundingClientRect().left;
                        var relativeY = e.pageY - this.canvas.getBoundingClientRect().top;
    
                        if (e.target == this.canvas && this.mdown) {
                            this.doMove(relativeX, relativeY);
                        }
    
                        if (relativeX <= 0 || relativeX >= this.canvas.clientWidth || relativeY <= 0 || relativeY >= this.canvas.clientHeight) {
                            this.mdown = false;
                        }
                    }.bind(this));
                },
    
                checkRequestAnimationFrame: function () {
                    var lastTime = 0;
                    var vendors = ['ms', 'moz', 'webkit', 'o'];
                    for (var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
                        window.requestAnimationFrame = window[vendors[x] + 'RequestAnimationFrame'];
                        window.cancelAnimationFrame = window[vendors[x] + 'CancelAnimationFrame']
                            || window[vendors[x] + 'CancelRequestAnimationFrame'];
                    }
    
                    if (!window.requestAnimationFrame) {
                        window.requestAnimationFrame = function (callback, element) {
                            var currTime = new Date().getTime();
                            var timeToCall = Math.max(0, 16 - (currTime - lastTime));
                            var id = window.setTimeout(function () {
                                callback(currTime + timeToCall);
                            }, timeToCall);
                            lastTime = currTime + timeToCall;
                            return id;
                        };
                    }
    
                    if (!window.cancelAnimationFrame) {
                        window.cancelAnimationFrame = function (id) {
                            clearTimeout(id);
                        };
                    }
                }
            }
            return {
                getInstance: getInstance,
                destroyInstance: destroyInstance,
                stopAnimate: stopAnimate,
                onZoom: onZoom
            };
        }]);

    imgZoomCanvas开放出了四个方法:

    getInstance (生成单体实例)
    destroyInstance (销毁单体实例)
    stopAnimate (停止requestAnimationFrame产生的动画)
    onZoom (图片缩放。传入缩放的参数,放大为正,缩小为负)

    imgUploader.js

    接下来介绍imgUpload这个directive,就是上面那个GIF看到的图片上传组件。逻辑代码封装在imgUploader.js里面你可以自己定制这四个方法:

    deleteAvatar(移除当前图片,并且上传到服务器)
    uploadAvatar(上传当前图片)
    zoomOut
    zoomIn
    (它们负责图片缩放的步长,为一正一负,参数可以自己慢慢调整。)

    另外该directive开放出一个回调方法onUpload,可以在你的业务controller里面实现相关图片上传logic。onUpload有三个参数:

     (image, isDelete, isHasAvatar)

    第一个image是从Canvas上面导出的base64具体的实现在刚才介绍的deleteAvatar中:
    canvas.toDataURL('png')

    imgUploader对应的html:

    <div id="img-uploader">
        <canvas class="avatar" id="myCanvas" width="150" height="150"></canvas>
        <div style="position: relative;left:156px;top:-1px">
            <div id="delete-avatar" ng-click="deleteAvatar()" class="delete-avatar">
                <img src="./image/delete.png">
            </div>
            <div class="edit-avatar">
                <img src="./image/edit.png">
    
                <div id="container" class="container">
                    <input class="file-picker" id="file" type="file"/>
                </div>
            </div>
            <div id="upload-avatar" ng-show="fileSelected" ng-click="uploadAvatar()" class="upload-avatar">
                <img src="./image/upload.png">
            </div>
            <div id="zoom-out" ng-show="fileSelected" ng-click="zoomOut()" class="zoom-out">
                <img src="./image/zoom_out.png">
            </div>
            <div id="zoom-in" ng-show="fileSelected" ng-click="zoomIn()" class="zoom-in">
                <img src="./image/zoom_in.png">
            </div>
        </div>
    </div>

    最后就是我们的页面逻辑代码了,页面controller中只需要实现上面提到的回调方法onUpload即可:

    $scope.upload = function (image, isDelete, isHasAvatar) {
          // Write your image upload logic here
     }

    页面html只需要加入我们刚才的directive imageUploader:

    <img-uploader on-upload="upload(image, isDelete, isHasAvatar)" image="image" is-editable="isEditable"></img-uploader>
    展开全文
  • 主要介绍了cropper js基于vue的图片裁剪上传功能的相关资料,需要的朋友可以参考下
  • 图片裁剪 上传

    2012-09-14 10:55:16
    图片裁剪+上传&&修正版
  • 本组件基于vuejs框架, 使用ES6基本语法, css预编译采用的scss, 图片裁剪模块基于cropperjs,拍照时的图片信息获取使用exif, 图片上传使用XMLHttpRequest 该组件已单独部署上线, 线上地址: upload-img.sufaith.com/, ...

    本组件基于vuejs框架, 使用ES6基本语法, css预编译采用的scss, 图片裁剪模块基于cropperjs,拍照时的图片信息获取使用exif, 图片上传使用XMLHttpRequest

    该组件已单独部署上线, 线上地址: upload-img.sufaith.com/, 图片最终是传至我个人的七牛云, 获取七牛云上传凭证token的接口是我单独做的一个nodejs服务, 可在PC或移动端打开测试下效果.

    涉及到的知识点整理如下:

    vuejs 介绍 — Vue.js

    scss Sass世界上最成熟、稳定和强大的CSS扩展语言 | Sass中文网

    cropperjs github.com/fengyuanche…

    exif github.com/exif-js/exi…

    XMLHttpRequest XMLHttpRequest()

     

    整体项目分成3个文件:

    1. uploadAvator.vue (父组件,用于选择图片,接收crop回调,执行上传)
    2. crop.vue (裁剪组件, 用于裁剪,压缩,回调裁剪结果给uploadAvator.vue)
    3. image.js (封装了基本的base64转换blob、获取图片url、xhr上传、图片压缩等方法)复制代码

     

    整体流程如下:

    1. input选择图片
    2. 调用cropperjs裁剪
    3. 修正方向, 压缩
    4. 上传

     

    具体实现步骤:

    一. 实现input选择文件

    1. 定义一个隐形样式的输入框,用于选择图片文件 (imgUrl初始化为默认图片地址)

    <template>
      <div class="upload-wrapper" :style="{backgroundImage: 'url(' + imgUrl + ')'}">
        <input @change="onChange" class="input" type="file" accept="image/jpg,image/jpeg,image/png,image/gif" multiple=""/>
      </div>
    </template>复制代码

     

    <style lang="scss" scoped>
    .upload-wrapper {
      position: relative;
      width: 77px;
      height: 77px;
      background-size: cover;
      border: 0;
      border-radius: 50%;
      margin: 20px auto;
      .input {
        position: absolute;
        z-index: 1;
        top: 0;
        left: 0;
        width: 100%;
        height: 100%;
        opacity: 0;
        -webkit-tap-highlight-color: rgba(0,0,0,0);
      }
    }
    </style>复制代码

    2. 对input选择图片做一些优化

    (1) 每次点击input选择图片时, 弹出选择文件的弹窗很慢,有些延迟

    解决方案: 明确定义input的accept属性对应的图片类型

    <input type="file" accept="image/jpg,image/jpeg,image/png,image/gif" multiple=""/>复制代码

    (2) 在ios设备下input若含有capture属性, 则只能调起相册,而安卓设备下input若不含capture属性,则只能调起相册

    解决方案: 判断是否为ios设备, 创建对应属性的input

    const UA = navigator.userAgent
    
    const isIpad = /(iPad).*OS\s([\d_]+)/.test(UA)
    const isIpod = /(iPod)(.*OS\s([\d_]+))?/.test(UA)
    const isIphone = !isIpad && /(iPhone\sOS)\s([\d_]+)/.test(UA)
    
    const isIos = isIpad || isIpod || isIphone
    复制代码

     

    <input v-if="isIos" @change="onChange" class="input" type="file" accept="image/jpg,image/jpeg,image/png,image/gif" multiple=""/>
    <!-- 安卓设备保留capture属性 -->
    <input v-else @change="onChange" class="input" type="file" accept="image/jpg,image/jpeg,image/png,image/gif" capture="camera" multiple=""/>
        复制代码

    (3) 再次点击input选择图片时, 若选择的图片和上一次选择的图片相同时,则不会触发onchange事件

    解决方案: 在每次接收到onchange事件时先销毁当前input, 再重新创建一个input, 此时可利用vue的v-if指令,轻松销毁或重建

    <input v-if="isIos && !destroyInput" @change="onChange" class="input" type="file" accept="image/jpg,image/jpeg,image/png,image/gif" multiple=""/>
    <!-- 安卓设备保留capture属性 -->
    <input v-if="!isIos && !destroyInput" @change="onChange" class="input" type="file" accept="image/jpg,image/jpeg,image/png,image/gif" capture="camera" multiple=""/>
        复制代码

     

    data() {
        return {
          destroyInput: false, // 是否销毁input元素, 解决在第二次和第一次选择的文件相同时不触发onchange事件的问题
          isIos: isIos // 是否为ios设备
        }
      },
    复制代码

    二. 调用cropperjs裁剪

    1. 获取选择的图片的url (用于裁剪)

    2. 获取拍照时的Orientation信息,解决拍出来的照片旋转问题

    3.显示裁剪组件并初始化

    4. 取消裁剪和开始裁剪

    三. 修正方向, 压缩并将base64回调给父组件

    const image = {}
    
    image.compress = function(img, Orientation) {
      // 图片压缩
      // alert('图片的朝向' + Orientation)
      let canvas = document.createElement('canvas')
      let ctx = canvas.getContext('2d')
      // 瓦片canvas
      let tCanvas = document.createElement('canvas')
      let tctx = tCanvas.getContext('2d')
      let initSize = img.src.length
      let width = img.width
      let height = img.height
    
      // 如果图片大于四百万像素,计算压缩比并将大小压至400万以下
      let ratio
      if ((ratio = width * height / 4000000) > 1) {
        console.log('大于400万像素')
        ratio = Math.sqrt(ratio)
        width /= ratio
        height /= ratio
      } else {
        ratio = 1
      }
      canvas.width = width
      canvas.height = height
      // 铺底色
      ctx.fillStyle = '#fff'
      ctx.fillRect(0, 0, canvas.width, canvas.height)
      // 如果图片像素大于100万则使用瓦片绘制
      let count
      if ((count = width * height / 1000000) > 1) {
        count = ~~(Math.sqrt(count) + 1) // 计算要分成多少块瓦片
        // 计算每块瓦片的宽和高
        let nw = ~~(width / count)
        let nh = ~~(height / count)
        tCanvas.width = nw
        tCanvas.height = nh
        for (let i = 0; i < count; i++) {
          for (let j = 0; j < count; j++) {
            tctx.drawImage(img, i * nw * ratio, j * nh * ratio, nw * ratio, nh * ratio, 0, 0, nw, nh)
            ctx.drawImage(tCanvas, i * nw, j * nh, nw, nh)
          }
        }
      } else {
        ctx.drawImage(img, 0, 0, width, height)
      }
      // 修复ios上传图片的时候 被旋转的问题
      if (Orientation && Orientation !== '' && Orientation !== 1) {
        switch (Orientation) {
          case 6: // 需要顺时针(向左)90度旋转
            image.rotateImg(img, 'left', canvas)
            break
          case 8: // 需要逆时针(向右)90度旋转
            image.rotateImg(img, 'right', canvas)
            break
          case 3: // 需要180度旋转
            image.rotateImg(img, 'right', canvas) // 转两次
            image.rotateImg(img, 'right', canvas)
            break
        }
      }
      // 设置jpegs图片的质量
      let ndata = canvas.toDataURL('image/jpeg', 1)
      console.log(`压缩前:${initSize}`)
      console.log(`压缩后:${ndata.length}`)
      console.log(`压缩率:${~~(100 * (initSize - ndata.length) / initSize)}%`)
      tCanvas.width = tCanvas.height = canvas.width = canvas.height = 0
      return ndata
    }
    
    image.rotateImg = function(img, direction, canvas) {
      // 图片旋转
      // 最小与最大旋转方向,图片旋转4次后回到原方向
      const minStep = 0
      const maxStep = 3
      if (img == null) return
      // img的高度和宽度不能在img元素隐藏后获取,否则会出错
      let height = img.height
      let width = img.width
      let step = 2
      if (step == null) {
        step = minStep
      }
      if (direction === 'right') {
        step++
        // 旋转到原位置,即超过最大值
        step > maxStep && (step = minStep)
      } else {
        step--
        step < minStep && (step = maxStep)
      }
      // 旋转角度以弧度值为参数
      let degree = step * 90 * Math.PI / 180
      let ctx = canvas.getContext('2d')
      switch (step) {
        case 0:
          canvas.width = width
          canvas.height = height
          ctx.drawImage(img, 0, 0)
          break
        case 1:
          canvas.width = height
          canvas.height = width
          ctx.rotate(degree)
          ctx.drawImage(img, 0, -height)
          break
        case 2:
          canvas.width = width
          canvas.height = height
          ctx.rotate(degree)
          ctx.drawImage(img, -width, -height)
          break
        case 3:
          canvas.width = height
          canvas.height = width
          ctx.rotate(degree)
          ctx.drawImage(img, -width, 0)
          break
      }
    }
    export default image
    复制代码

    四. 上传图片

    1. base64转换为文件

    2. XMLHttpRequest上传

    3. 定义上传状态的样式,包括上传进度和上传失败的标识

    4.父组件接收到裁剪组件的回调的base64后,执行上传

    <template>
    <div>
      <div class="upload-wrapper" :class="{'upload-status-bg': showStatusWrapper}" :style="{backgroundImage: 'url(' + imgUrl + ')'}">
        <input v-if="isIos && !destroyInput" @change="onChange" class="input" type="file" accept="image/jpg,image/jpeg,image/png,image/gif" multiple=""/>
        <!-- 安卓设备保留capture属性 -->
        <input v-if="!isIos && !destroyInput" @change="onChange" class="input" type="file" accept="image/jpg,image/jpeg,image/png,image/gif" capture="camera" multiple=""/>
        <!-- 上传状态 -->
        <div v-if="showStatusWrapper" class="upload-status-wrapper">
          <i class="fail" v-if="showStatusFail">!</i>
          <i v-else>{{procent}}%</i>
        </div>
      </div>
      <crop ref="cropWrapper" v-show="showCrop" @hide="showCrop=false" @finish="setUpload"></crop>
    </div>
    </template>
    复制代码

    福利: 本文已同步到我的个人技术网站 IT干货-sufaith 该网站包括Python, Linux, Nodejs, 前端开发等模块, 专注于程序开发中的技术、经验总结与分享, 欢迎访问.

    展开全文
  • jQuery(文件上传|图片裁剪上传)渣渣插件,支持拖拽,粘贴上传,上传进度 API var upload = new Upload ( root ) ; //图片裁剪上传 upload . clipUpload ( options ) //文件上传 upload . fileUpload ( ...
  • PHP结合HTML5实现无刷新的图片裁剪上传功能,实现的功能类似QQ截屏效果,可鼠标圈选截图区域,适时显示截图区域信息,选择好截屏区域后,点击“upload”按钮,即可将截取的图像上传到服务器。
  • 但是浏览器原生的文件上传按钮的颜值不尽人意,而且按钮上的文字是无法改变的,我需要把这个上传文件的按钮改造一下。 方法1:使用label元素来触发一个隐藏的file类型的 input元素;(缺点:在多人开发时,可能出现...
  • 从事安卓开发5年,这是我找到的最好的图片裁剪工具,在做头像上传时候非常有用。 它支持缩放,旋转(大多数其他工具都不支持),缩略等功能。
  • 使用cropper.js裁剪图片,通过canvas获取裁剪后的图片,获取的是base64图片上传
  • 基于vue的社交分享插件
  • jQuery点击弹出图片裁剪上传插件
  • NULL 博文链接:https://qihaha.iteye.com/blog/2068868
  • 最简单纯js+html5选择图片裁剪上传服务端简单示例,参数控制裁剪区域大小,裁剪区域可移动,支持裁剪后预览,提供服务端源码PHP文件接收上传示例。
  • 可用于头像等的图片上传处理,打开图片文件后,支持图片的拖动、图片放大和缩小;可获取到base64资源,也可以上传到自己服务器获取普通图片地址。 本资源经过本人测试、改进,没有做进一部的封装。可直接使用,如有...
  • 图片裁剪上传插件—jquery.photoClip.js

    万次阅读 2018-01-29 16:29:38
    分别介绍了两种插件 1.cropper.js ... ...接下来只是实现一个简单的功能:网页中可以上传图片,然后对图片进行裁剪,点击确定后会显示出裁剪后的图片裁剪图片  .row{  

    分别介绍了两种插件

    1.cropper.js

    具体详情:https://segmentfault.com/a/1190000012344970

    (1)在页面直接使用cropper    

    接下来只是实现一个简单的功能:网页中可以上传图片,然后对图片进行裁剪,点击确定后会显示出裁剪后的图片。
    <!DOCTYPE html>
    <html lang="zh-cn">
    <head>
    <meta charset="UTF-8">
    <title>裁剪图片</title>
    <link href="https://cdn.bootcss.com/cropper/3.1.3/cropper.min.css" rel="stylesheet">
    <link href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
    <style>
            .row{
                margin-bottom: 5px;
            }
            #photo {
                max-width: 100%;
            }
            .img-preview {
                width: 100px;
                height: 100px;
                overflow: hidden;
            }
            button {
                margin-top:10px;
            }
            #result {
                width: 150px;
                height: 150px;
            }
    </style>
    </head>
    <body>
    <div class="container">
        <div class="row">
            <div class="col-sm-12 text-center">
                <label for="input" class="btn btn-danger" id="">
                <span>选择图片</span>
                <input type="file" id="input" class="sr-only">
                </label>
            </div>
        </div>
        <div class="row">
            <div class="col-sm-6 col-sm-offset-2">
                <img src="" id="photo">
            </div>
            <div class="col-sm-2">
                <div>
                    <p>
                        预览(100*100):
                    </p>
                    <div class="img-preview">
                    </div>
                </div>
                <button class="btn btn-primary" οnclick="crop()">裁剪图片</button>
                <div>
                    <br/>
                    <p>
                        结果:
                    </p>
                    <img src="" alt="裁剪结果" id="result">
                </div>
            </div>
        </div>
    </div>
    <!-- Scripts -->
    <script src="https://cdn.bootcss.com/jquery/3.2.1/jquery.min.js"></script>
    <script src="https://cdn.bootcss.com/cropper/3.1.3/cropper.min.js"></script>
    <script src="https://cdn.bootcss.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
    <script>
            // 修改自官方demo的js
            var initCropper = function (img, input){
                var $image = img;
                var options = {
                    aspectRatio: 1, // 纵横比
                    viewMode: 2,
                    preview: '.img-preview' // 预览图的class名
                };
                $image.cropper(options);
                var $inputImage = input;
                var uploadedImageURL;
                if (URL) {
                    // 给input添加监听
                    $inputImage.change(function () {
                        var files = this.files;
                        var file;
                        if (!$image.data('cropper')) {
                            return;
                        }
                        if (files && files.length) {
                            file = files[0];
                            // 判断是否是图像文件
                            if (/^image\/\w+$/.test(file.type)) {
                                // 如果URL已存在就先释放
                                if (uploadedImageURL) {
                                    URL.revokeObjectURL(uploadedImageURL);
                                }
                                uploadedImageURL = URL.createObjectURL(file);
                                // 销毁cropper后更改src属性再重新创建cropper
                                $image.cropper('destroy').attr('src', uploadedImageURL).cropper(options);
                                $inputImage.val('');
                            } else {
                              window.alert('请选择一个图像文件!');
                          }
                      }
                  });
                } else {
                    $inputImage.prop('disabled', true).addClass('disabled');
                }
            }
            var crop = function(){
                var $image = $('#photo');
                var $target = $('#result');
                $image.cropper('getCroppedCanvas',{
                    width:300, // 裁剪后的长宽
                    height:300
                }).toBlob(function(blob){
                    // 裁剪后将图片放到指定标签
                    $target.attr('src', URL.createObjectURL(blob));
                });
            }
            $(function(){
                initCropper($('#photo'),$('#input'));
            });
        </script>
    </body>
    </html>

    (2)在bootstrap模态框中使用cropper

    <!DOCTYPE html>
    <html lang="zh-cn">
    <head>
    <meta charset="UTF-8">
    <title>上传头像</title>
    <link href="https://cdn.bootcss.com/cropper/3.1.3/cropper.min.css" rel="stylesheet">
    <link href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
    <style type="text/css">
        body{
            text-align: center;
        }
        #user-photo {
            width:300px;
            height:300px;
            margin-top: 10px;
        }
        #photo {
            max-width:100%;
            max-height:350px;
        }
        .img-preview-box {
            text-align: center;
        }
        .img-preview-box > div {
            display: inline-block;;
            margin-right: 10px;
        }

        .img-preview {
            overflow: hidden;
        }
        .img-preview-box .img-preview-lg {
            width: 150px;
            height: 150px;
        }
        .img-preview-box .img-preview-md {
            width: 100px;
            height: 100px;
        }
        .img-preview-box .img-preview-sm {
            width: 50px;
            height: 50px;
            border-radius: 50%;
        }
    </style>
    </head>
    <body>
    <button class="btn btn-primary" data-target="#changeModal" data-toggle="modal">打开</button><br/>
    <div class="user-photo-box">
        <img id="user-photo" src="">
    </div>
    </div>
    <div class="modal fade" id="changeModal" tabindex="-1" role="dialog" aria-hidden="true">
    <div class="modal-dialog">
        <div class="modal-content">
            <div class="modal-header">
                <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
                <h4 class="modal-title text-primary">
                <i class="fa fa-pencil"></i>
                            更换头像
                </h4>
            </div>
            <div class="modal-body">
                <p class="tip-info text-center">
                    未选择图片
                </p>
                <div class="img-container hidden">
                    <img src="" alt="" id="photo">
                </div>
                <div class="img-preview-box hidden">
                    <hr>
                    <span>150*150:</span>
                    <div class="img-preview img-preview-lg">
                    </div>
                    <span>100*100:</span>
                    <div class="img-preview img-preview-md">
                    </div>
                    <span>30*30:</span>
                    <div class="img-preview img-preview-sm">
                    </div>
                </div>
            </div>
            <div class="modal-footer">
                <label class="btn btn-danger pull-left" for="photoInput">
                <input type="file" class="sr-only" id="photoInput" accept="image/*">
                <span>打开图片</span>
                </label>
                <button class="btn btn-primary disabled" disabled="true" οnclick="sendPhoto();">提交</button>
                <button class="btn btn-close" aria-hidden="true" data-dismiss="modal">取消</button>
            </div>
        </div>
    </div>
    </div>
    <script src="https://cdn.bootcss.com/jquery/3.2.1/jquery.min.js"></script>
    <script src="https://cdn.bootcss.com/cropper/3.1.3/cropper.min.js"></script>
    <script src="https://cdn.bootcss.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
    <script type="text/javascript">
            var initCropperInModal = function(img, input, modal){
                var $image = img;
                var $inputImage = input;
                var $modal = modal;
                var options = {
                    aspectRatio: 1, // 纵横比
                    viewMode: 2,
                    preview: '.img-preview' // 预览图的class名
                };
                // 模态框隐藏后需要保存的数据对象
                var saveData = {};
                var URL = window.URL || window.webkitURL;
                var blobURL;
                $modal.on('show.bs.modal',function () {
                    // 如果打开模态框时没有选择文件就点击“打开图片”按钮
                    if(!$inputImage.val()){
                        $inputImage.click();
                    }
                }).on('shown.bs.modal', function () {
                    // 重新创建
                    $image.cropper( $.extend(options, {
                        ready: function () {
                            // 当剪切界面就绪后,恢复数据
                            if(saveData.canvasData){
                                $image.cropper('setCanvasData', saveData.canvasData);
                                $image.cropper('setCropBoxData', saveData.cropBoxData);
                            }
                        }
                    }));
                }).on('hidden.bs.modal', function () {
                    // 保存相关数据
                    saveData.cropBoxData = $image.cropper('getCropBoxData');
                    saveData.canvasData = $image.cropper('getCanvasData');
                    // 销毁并将图片保存在img标签
                    $image.cropper('destroy').attr('src',blobURL);
                });
                if (URL) {
                    $inputImage.change(function() {
                        var files = this.files;
                        var file;
                        if (!$image.data('cropper')) {
                            return;
                        }
                        if (files && files.length) {
                            file = files[0];
                            if (/^image\/\w+$/.test(file.type)) {


                                if(blobURL) {
                                    URL.revokeObjectURL(blobURL);
                                }
                                blobURL = URL.createObjectURL(file);


                                // 重置cropper,将图像替换
                                $image.cropper('reset').cropper('replace', blobURL);


                                // 选择文件后,显示和隐藏相关内容
                                $('.img-container').removeClass('hidden');
                                $('.img-preview-box').removeClass('hidden');
                                $('#changeModal .disabled').removeAttr('disabled').removeClass('disabled');
                                $('#changeModal .tip-info').addClass('hidden');


                            } else {
                                window.alert('请选择一个图像文件!');
                            }
                        }
                    });
                } else {
                    $inputImage.prop('disabled', true).addClass('disabled');
                }
            }
        }


        var sendPhoto = function(){
            $('#photo').cropper('getCroppedCanvas',{
                width:300,
                height:300
            }).toBlob(function(blob){
                // 转化为blob后更改src属性,隐藏模态框
                $('#user-photo').attr('src',URL.createObjectURL(blob));
                $('#changeModal').modal('hide');
            });
        }


        $(function(){
            initCropperInModal($('#photo'),$('#photoInput'),$('#changeModal'));
        });
    </script>
    </body>
    </html>


      

    使用cropper来上传图片到服务器

    由于cropper可以得到两种裁剪后图片的数据(即blob和dataURL),所以对应的上传到后台也会有两种方法,在这里我只写一种使用ajax上传base64 dataURL的,另一种方法如果有兴趣,可以自己尝试。

    页面中,将上面的sendPhoto方法改为:

    上传方式(1):

    var sendPhoto = function () {
        // 得到PNG格式的dataURL
        var photo = $('#photo').cropper('getCroppedCanvas', {
            width: 300,
            height: 300
        }).toDataURL('image/png');
    
        $.ajax({
            url: '上传地址', // 要上传的地址
            type: 'post',
            data: {
                'imgData': photo
            },
            dataType: 'json',
            success: function (data) {
                if (data.status == 0) {
                    // 将上传的头像的地址填入,为保证不载入缓存加个随机数
                    $('.user-photo').attr('src', '头像地址?t=' + Math.random());
                    $('#changeModal').modal('hide');
                } else {
                    alert(data.info);
                }
            }
        });
    }

    上传方式(2)
    通过$img.cropper(“getCroppedCanvas”)获取到canvas并调用其toBlob方法将数据转化为二进制用来构造FormData
    <script>
    $img.cropper("getCroppedCanvas").toBlob(function(blob){
    var formData=new FormData();
    formData.append('files',blob,file.name);
    $.ajax({
    method:"post",
    url: urlConfig.upload, //用于文件上传的服务器端请求地址
    data: formData,
    processData: false,
    contentType: false,
    success:function(result){
    //do something
    }
    });
    });
    </script>




    后台中,Java的主要代码如下:(使用了jdk8的Base64,,如果是低版本请自行替换)

    /**
         * 将Base64位编码的图片进行解码,并保存到指定目录
         */
        public static void decodeBase64DataURLToImage(String dataURL, String path, String imgName) throws IOException {
            // 将dataURL开头的非base64字符删除
            String base64 = dataURL.substring(dataURL.indexOf(",") + 1);
            FileOutputStream write = new FileOutputStream(new File(path + imgName));
            byte[] decoderBytes = Base64.getDecoder().decode(base64);
            write.write(decoderBytes);
            write.close();
        }









    2.photoClip.js

    需要依赖的的插件为:

    [jquery.transit.js] 插件 (v1.4 中已经移除了对该插件的依赖)

    [iscroll-zoom.js] 插件(由于原插件的zoom扩展存在几个bug,所以建议使用demo中提供的iscroll-zoom.js文件,本人已经将这些bug修复)

    [hammer.js] 插件 

    [lrz.all.bundle.js] 插件

    参数讲解:

    < div  id = "clipArea" ></ div >
    < input  type = "file"  id = "file" >
    < button  id = "clipBtn" >截取</ button >
    < div  id = "view" ></ div >
    < script  src = "js/jquery-2.1.3.min.js" ></ script >
    < script  src = "js/hammer.min.js" ></ script >
    < script  src = "js/iscroll-zoom.min.js" ></ script >
    < script  src = "js/lrz.all.bundle.js" ></ script >
    < script  src = "js/jquery.photoClip.min.js" ></ script >
    < script >
    var clipArea = new bjj.PhotoClip("#clipArea", {
         size: [260, 260], // 截取框的宽和高组成的数组。默认值为[260,260]
         outputSize: [640, 640], // 输出图像的宽和高组成的数组。默认值为[0,0],表示输出图像原始大小
         //outputType: "jpg", // 指定输出图片的类型,可选 "jpg" 和 "png" 两种种类型,默认为 "jpg"
         file: "#file", // 上传图片的< input  type = "file" >控件的选择器或者DOM对象
         view: "#view", // 显示截取后图像的容器的选择器或者DOM对象
         ok: "#clipBtn", // 确认截图按钮的选择器或者DOM对象
         loadStart: function(file) {}, // 开始加载的回调函数。this指向 fileReader 对象,并将正在加载的 file 对象作为参数传入
         loadComplete: function(src) {}, // 加载完成的回调函数。this指向图片对象,并将图片地址作为参数传入
         loadError: function(event) {}, // 加载失败的回调函数。this指向 fileReader 对象,并将错误事件的 event 对象作为参数传入
         clipFinish: function(dataURL) {}, // 裁剪完成的回调函数。this指向图片对象,会将裁剪出的图像数据DataURL作为参数传入
    });
    </ script >

    展开全文
  • Flutter自定义控件之图片裁剪

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 17,449
精华内容 6,979
关键字:

图片裁剪上传