博客
关于我
微信小程序可移动缩放图片裁剪框
阅读量:301 次
发布时间:2019-03-01

本文共 7786 字,大约阅读时间需要 25 分钟。

微信小程序图片裁剪框开发实践指南

效果预览

本文将详细介绍一个基于微信小程序canvas组件实现的可移动缩放图片裁剪框的开发方法,结合实际项目经验分享技术细节和实现方案。

前言

传统的图片裁剪框应用在移动端应用中虽然功能完善,但在灵活性和精准度方面仍存在一定局限性。通过使用微信小程序canvas组件,可以实现一个支持自由移动和缩放的图片裁剪框解决方案。本文将从技术分析、实现细节等多个方面展开讨论。

技术要点分析

使用canvas组件实现图片裁剪框开发涉及多个关键技术点,包括:

  • 图片绘制与裁剪框绘制:通过canvasAPI实现图片绘制和裁剪框边框绘制。
  • 裁剪框缩放点计算:计算四个缩放点的位置,确保裁剪框的灵活性和精准性。
  • 裁剪框移动与缩放:实现基于触摸事件的裁剪框操作,支持自由移动和缩放。
  • 边界防超出控制:确保裁剪框不会超出屏幕边界,提供更好的用户体验。
  • 重绘机制优化:在裁剪框发生变化时,及时重绘canvas,避免重叠或其他显示问题。
  • 裁剪结果生成:将裁剪后的图片区域转换为临时文件,方便后续使用。
  • 具体实现

    1. 页面数据结构

    page组件的数据结构设计如下:

    data: {
    open_camera: boolean, // 是否打开相机拍照
    cut_image_canvas: {}, //裁剪画布上下文
    mycanvas: {}, // canvas 实例
    image_obj: {}, //绘制的图片对象
    window_width: number, //屏幕宽度
    window_height: number, //屏幕高度
    cut_img_canvas_w: number, //画布宽度
    cut_img_canvas_h: number, //画布高度
    cut_area: { //裁剪区域配置
    x: number, //左上角顶点x坐标
    y: number, //左上角顶点y坐标
    cut_width: number, //裁剪宽度
    cut_height: number, //裁剪高度
    cut_area_color: string, //裁剪框边框颜色
    },
    pixelRatio: number, //像素比
    last_touches_x: number, //上一个触摸点x坐标
    last_touches_y: number, //上一个触摸点y坐标
    cut_area_change_status: string, //裁剪框变换类型
    cut_image: string, //裁剪后的图片临时文件链接
    }

    2. WXML、WXSS和页面数据初始化

    page组件中,初始化屏幕参数和canvas布局:

    裁剪
    拍照
    .take_photo {
    width: 240rpx;
    height: 96rpx;
    background: white;
    z-index: 1000;
    display: flex;
    flex-direction: row;
    align-content: center;
    align-items: center;
    justify-content: space-around;
    justify-items: center;
    border-radius: 10rpx;
    background: rgba(0, 0, 0, 0.4);
    color: white;
    position: fixed;
    bottom: 8%;
    left: 35%;
    text-align: center;
    font-size: 30rpx;
    }
    .take_photo_hover {
    transform: scale(0.9);
    }

    3. 创建canvas绘制图片和裁剪框

    page组件中,初始化canvas并绘制图片和裁剪框:

    createCutImageCanvas(imageUrl) {
    const that = this;
    const query = that.createSelectorQuery();
    query.select('#cut_image_canvas').fields({
    node: true,
    size: true
    }).exec((res) => {
    const canvas = res[0].node;
    that.data.mycanvas = canvas;
    that.data.cut_image_canvas = canvas.getContext('2d');
    wx.getImageInfo({
    src: imageUrl,
    success: (res) => {
    const w = that.data.window_width;
    const h = that.data.window_height;
    const imgWidth = res.width;
    const imgHeight = res.height;
    canvas.width = w;
    canvas.height = h;
    that.data.cut_area.cut_width = 0.8 * w;
    that.data.cut_area.x = (w - that.data.cut_area.cut_width) / 2;
    that.data.cut_area.y = (h - that.data.cut_area.cut_height) / 2;
    that.setData({
    cut_img_canvas_h: h,
    cut_img_canvas_w: w
    });
    that.data.image_obj = canvas.createImage();
    that.data.image_obj.src = imageUrl;
    that.data.image_obj.onload = () => {
    that.data.cut_image_canvas.drawImage(that.data.image_obj, 0, 0, w, h);
    that.createCutArea();
    };
    }
    });
    });
    }

    4. 绘制裁剪框

    page组件中,绘制裁剪框:

    createCutArea() {
    this.data.cut_image_canvas.strokeStyle = this.data.cut_area.cut_area_color;
    this.data.cut_image_canvas.strokeRect(
    this.data.cut_area.x,
    this.data.cut_area.y,
    this.data.cut_area.cut_width,
    this.data.cut_area.cut_height
    );
    // 绘制四个缩放点
    this.data.cut_image_canvas.beginPath();
    this.data.cut_image_canvas.arc(
    this.data.cut_area.x,
    this.data.cut_area.y,
    5 * this.data.pixelRatio,
    0,
    2 * Math.PI
    );
    this.data.cut_image_canvas.fillStyle = 'white';
    this.data.cut_image_canvas.fill();
    this.data.cut_image_canvas.beginPath();
    this.data.cut_image_canvas.arc(
    this.data.cut_area.x,
    this.data.cut_area.y,
    2.5 * this.data.pixelRatio,
    0,
    2 * Math.PI
    );
    this.data.cut_image_canvas.fillStyle = '#1296db';
    this.data.cut_image_canvas.fill();
    // 其他三个缩放点类似绘制...
    }

    5. 辨别裁剪框移动和缩放手势

    page组件中,处理触摸事件:

    getCutAreaChangeStatus(e) {
    const that = this;
    const x = e.touches[0].x;
    const y = e.touches[0].y;
    that.data.cut_area_change_status = '';
    const scalePointLocation = that.getScalePointLocation();
    const scaleResponseRadius = 30;
    // 判断是否在移动区域
    if ((scalePointLocation.left_up_point_x + scaleResponseRadius < x &&
    x < scalePointLocation.right_down_point_x - scaleResponseRadius &&
    scalePointLocation.left_up_point_y < y &&
    y < scalePointLocation.right_down_point_y) {
    that.data.cut_area_change_status = 'move';
    }
    // 判断是否在缩放区域
    if (Math.sqrt(
    Math.pow(x - scalePointLocation.left_up_point_x, 2) +
    Math.pow(y - scalePointLocation.left_up_point_y, 2)
    ) < scaleResponseRadius) {
    that.data.cut_area_change_status = 'left_up_scale';
    }
    // 类似处理其他三个缩放点...
    }

    6. canvas重绘

    page组件中,实现canvas重绘:

    redraw(mode = 0) {
    this.data.cut_image_canvas.clearRect(0, 0, this.data.cut_img_canvas_w, this.data.cut_img_canvas_h);
    this.data.cut_image_canvas.drawImage(this.data.image_obj, 0, 0,
    this.data.cut_img_canvas_w, this.data.cut_img_canvas_h);
    if (mode === 0) {
    this.createCutArea();
    }
    }

    7. 防止裁剪框超出屏幕

    page组件中,实现裁剪框边界控制:

    watchCutAreaOverflow() {
    const that = this;
    const margin = 5 * that.data.pixelRatio;
    const scalePointLocation = that.getScalePointLocation();
    if (scalePointLocation.left_up_point_x < margin) return 1;
    if (scalePointLocation.right_down_point_x > (that.data.window_width - margin)) return 1;
    if (scalePointLocation.left_up_point_y < margin) return 1;
    if (scalePointLocation.right_down_point_y > (that.data.window_height - margin)) return 1;
    return 0;
    }

    8. 裁剪框移动和缩放

    page组件中,处理触摸移动事件:

    cutAreaMoveAndScale(e) {
    const that = this;
    const touch_x = e.touches[0].x;
    const touch_y = e.touches[0].y;
    const dx = touch_x - that.data.last_touches_x;
    const dy = touch_y - that.data.last_touches_y;
    if (that.data.cut_area_change_status === 'left_up_scale' ||
    that.data.cut_area_change_status === 'left_down_scale') {
    that.data.cut_area.cut_height += -dy;
    that.data.cut_area.cut_width += -dx;
    that.data.cut_area.x += dx;
    that.data.cut_area.y += dy;
    if (that.watchCutAreaOverflow()) {
    that.data.cut_area.cut_height -= -dy;
    that.data.cut_area.cut_width -= -dx;
    that.data.cut_area.x -= dx;
    that.data.cut_area.y -= dy;
    return;
    }
    that.redraw();
    }
    if (that.data.cut_area_change_status === 'right_up_scale' ||
    that.data.cut_area_change_status === 'right_down_scale') {
    that.data.cut_area.cut_height += dy;
    that.data.cut_area.cut_width += dx;
    if (that.watchCutAreaOverflow()) {
    that.data.cut_area.cut_width -= dx;
    that.data.cut_area.cut_height -= dy;
    return;
    }
    that.redraw();
    }
    if (that.data.cut_area_change_status === 'move') {
    that.data.cut_area.x += dx;
    that.data.cut_area.y += dy;
    if (that.watchCutAreaOverflow()) {
    that.data.cut_area.x -= dx;
    that.data.cut_area.y -= dy;
    return;
    }
    that.redraw();
    }
    that.data.last_touches_x = touch_x;
    that.data.last_touches_y = touch_y;
    }

    9. 生成裁剪后的图片及预览

    page组件中,处理裁剪图片:

    cutImage() {
    this.redraw(1); // 1表示不绘制裁剪框
    wx.canvasToTempFilePath({
    x: this.data.cut_area.x / this.data.pixelRatio,
    y: this.data.cut_area.y / this.data.pixelRatio,
    width: Math.round(this.data.cut_area.cut_width / this.data.pixelRatio),
    height: Math.round(this.data.cut_area.cut_height / this.data.pixelRatio),
    canvas: this.data.mycanvas,
    filetype: 'png',
    success: (res) => {
    console.log('裁剪的图片', res.tempFilePath);
    this.setData({
    cut_image: res.tempFilePath
    });
    },
    fail: (err) => {
    console.log('裁剪图片失败', err);
    wx.showModal({
    title: '错误',
    content: '剪切图片失败'
    });
    }
    });
    }

    集成到项目

    以上代码已经封装成一个自定义组件,方便集成到项目中。如果需要直接在page中使用,可以直接复制以上代码到相应的page中。自定义组件下载地址:链接 提取码:wbau

    转载地址:http://uhet.baihongyu.com/

    你可能感兴趣的文章
    Nginx 负载均衡详解
    查看>>
    nginx 配置 单页面应用的解决方案
    查看>>
    nginx 配置https(一)—— 自签名证书
    查看>>
    nginx 配置~~~本身就是一个静态资源的服务器
    查看>>
    Nginx 配置清单(一篇够用)
    查看>>
    Nginx 配置解析:从基础到高级应用指南
    查看>>
    nginx+php的搭建
    查看>>
    nginx+tomcat+memcached
    查看>>
    nginx+Tomcat性能监控
    查看>>
    nginx+uwsgi+django
    查看>>
    Nginx-http-flv-module流媒体服务器搭建+模拟推流+flv.js在前端html和Vue中播放HTTP-FLV视频流
    查看>>
    Nginx下配置codeigniter框架方法
    查看>>
    Nginx之二:nginx.conf简单配置(参数详解)
    查看>>
    Nginx代理websocket配置(解决websocket异常断开连接tcp连接不断问题)
    查看>>
    Nginx代理初探
    查看>>
    Nginx代理外网映射
    查看>>
    Nginx代理模式下 log-format 获取客户端真实IP
    查看>>
    Nginx代理静态资源(gis瓦片图片)实现非固定ip的url适配网络环境映射ip下的资源请求解决方案
    查看>>
    Nginx反向代理与正向代理配置
    查看>>
    Nginx多域名,多证书,多服务配置,实用版
    查看>>