这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助

最近公司出了一个新的功能模块(如下图),大提上可以描述为实现拍照完上传图片,拖动四方框拍照完成上传功能,大体样子如下图。但是我找遍了 dcloud 插件市场,找到的插件都是移动背景图片来实现裁剪的,跟京东的功能是相反的,没办法只能自己来实现这么一个插件。

第一步

首先就需要实现一个四方框的功能了。从上图可知,四方框有一下几个特点

  1. 四个角粘连外框,随着框的大小和移动范围紧缚移动
  2. 四方框可随意四个方向拖动
  3. 方框外区域阴影不影响方框内

那么我们根据这个特性来实现下这个功能,对于 css 规范的话使用 bem 规范

<div class="clip__content">
<div v-for="(item, index) in 4" :key="index" class="clip__edge"></div>
</div>

/more

$edge-border-width: 6rpx;
.clip {
&__content {
position: fixed;
width: 400rpx;
height: 400rpx;
left: 0;
top: 0;
border: 1px solid red;
z-index: 4;
overflow: hidden;
box-shadow: rgba(0, 0, 0, 0.5) 0 0 0 200vh;
}
&__edge {
position: absolute;
width: 34rpx;
height: 34rpx;
border: 10rpx solid red;
pointer-events: auto;
z-index: 2;
&::before {
content: "";
position: absolute;
z-index: 2;
width: 40rpx;
height: 40rpx;
background-color: transparent;
}
&:nth-child(1) {
left: $edge-border-width;
top: $edge-border-width;
border-bottom-width: 0 !important;
border-right-width: 0 !important;
&:before {
top: -50%;
left: -50%;
}
}
&:nth-child(2) {
right: $edge-border-width;
top: $edge-border-width;
border-bottom-width: 0 !important;
border-left-width: 0 !important;
&:before {
top: -50%;
left: 50%;
}
}
&:nth-child(3) {
left: $edge-border-width;
bottom: $edge-border-width;
border-top-width: 0 !important;
border-right-width: 0 !important;
&:before {
bottom: -50%;
left: -50%;
}
}
&:nth-child(4) {
right: $edge-border-width;
bottom: $edge-border-width;
border-top-width: 0 !important;
border-left-width: 0 !important;
&:before {
bottom: -50%;
left: 50%;
}
}
}

根据上面的 html 和 css 出来的样式大概如下图 外部的阴影效果我们用: box-shadow: rgba(0, 0, 0, 0.5) 0 0 0 200vh 来达成

第二步

第二步的话就要实现移动功能了,这里是一个比较考验耐心的地方,因为涉及到多个方向的变化,需要不断地进行调试,在此之前需要先分析下四个角变化的特性,下面先看 4 个角的移动特性(以 H5 思维)

  1. 第一个角的移动会改变方框的 left,top,width,right4 个值
  2. 第二个角的移动会改变方框的 top,with,height3 个值
  3. 第三个角的移动会改变方框的 left, width,height3 个值
  4. 第四个角的移动会改变方框的 width,height2 个值
  5. 四个角的移动都不能小于 4 个角的宽高,四个角的移动都不能超过屏幕,相应的逻辑需要做一下限制

首先需要获取下屏幕宽度,区域高度(因为头部可能会有导航栏目占位,所以不拿屏幕高度),四方框初始宽高,

uni.getSystemInfo({
success: res => {
console.log(res)
this.systemInfo = res
}
})
uni
.createSelectorQuery()
.select('.clip__content')
.fields({ size: true }, data => {
this.width = data.width
this.height = data.height
})
.exec()
uni
.createSelectorQuery()
.select('.clip')
.fields({ size: true }, data => {
this.screenHeight = data.height
})
.exec()

后续的话就可以进行四个角拖拽了,这里用到了 touchStart 和 touchMove,动态地为方框绑定样式

<div
v-for="(item, index) in 4"
class="clip__edge"
@touchstart.stop.prevent="edgeTouchStart"
@touchmove.stop.prevent="e => edgeTouchMove(e, index)"
@touchend.stop.prevent="edgeTouchEnd"
></div>

接下来开始写逻辑

edgeTouchStart(e) {
// 记录坐标xy初始位置
this.clientX = e.changedTouches[0].clientX;
this.clientY = e.changedTouches[0].clientY;
},
edgeTouchMove(e, index) {
const currX = e.changedTouches[0].clientX;
const currY = e.changedTouches[0].clientY;
// 记录坐标差
const moveX = currX - this.clientX;
const moveY = currY - this.clientY;
// 更新坐标位置
this.clientX = currX;
this.clientY = currY;
const { width, height, left, top, screenHeight } = this;
const { screenWidth } = this.systemInfo;
// 初始化最大宽高
let maxWidth = 0,
maxHeight = 0,
maxTop = top + moveY < 0 ? 0 : top + moveY,
maxLeft = left + moveX < 0 ? 0 : left + moveX;
// 四个角的宽高限制
if (index % 2 === 0) {
maxWidth = width - moveX > screenWidth ? screenWidth : width - moveX;
} else {
maxWidth = width + moveX > screenWidth ? screenWidth : width + moveX;
}
if (index < 2) {
maxHeight =
height - moveY > screenHeight ? screenHeight : height - moveY;
} else {
maxHeight =
height + moveY > screenHeight ? screenHeight : height + moveY;
} // 四个角的规则计算逻辑 四边方框暂定40 更详细的要用.createSelectorQuery()去拿
if (index === 0) {
if (width - moveX <= 40 || height - moveY <= 40) return;
console.log(maxLeft);
this.clipStyle = {
width: maxWidth,
height: maxHeight,
left: maxLeft,
top: maxTop,
};
this.width = maxWidth;
this.height = maxHeight;
this.top = maxTop;
this.left = maxLeft;
// 右上角
} else if (index === 1) {
if (width + moveX <= 40 || height - moveY <= 40) return;
this.clipStyle = {
width: maxWidth,
height: maxHeight,
left,
top: maxTop,
}; this.width = maxWidth;
this.height = maxHeight;
this.top = maxTop;
} else if (index === 2) {
if (width - moveX <= 40 || height + moveY <= 40) return;
this.clipStyle = {
width: maxWidth,
height: maxHeight,
left: maxLeft,
top,
}; this.width = maxWidth;
this.height = maxHeight;
this.left = maxLeft;
} else if (index === 3) {
if (width + moveX <= 40 || height + moveY <= 40) return;
this.clipStyle = {
width: maxWidth,
height: maxHeight,
left,
top,
}; this.width = maxWidth;
this.height = maxHeight;
}
}

效果如下图

第三步

四个角拖拽逻辑完善之后,下一步目标就是做四方框的拖拽,这边需要对四方框的拖拽做一次限制

<div
class="clip__content"
:style="style"
@touchstart.stop.prevent="clipTouchStart"
@touchmove.stop.prevent="clipTouchMove"
>
...
</div>
clipTouchStart(e) {
this.touchX = e.changedTouches[0].pageX;
this.touchY = e.changedTouches[0].pageY;
},
clipTouchMove(e) {
const { screenWidth } = this.systemInfo;
const currX = e.changedTouches[0].pageX;
const currY = e.changedTouches[0].pageY;
const moveX = currX - this.touchX;
const moveY = currY - this.touchY;
this.touchX = currX;
this.touchY = currY;
// 边框限制逻辑
if (this.left + moveX < 0) {
this.left = 0;
} else if (this.left + moveX > screenWidth - this.width) {
this.left = screenWidth - this.width;
} else {
this.left = this.left + moveX;
}
if (this.top + moveY < 0) {
this.top = 0;
} else if (this.top + moveY > this.screenHeight - this.height) {
this.top = this.screenHeight - this.height;
} else {
this.top = this.top + moveY;
}
this.clipStyle = {
...this.clipStyle,
left: this.left,
top: this.top,
};
},

效果如下图:

第四步就是做我们的截图了,这里用到了 canvas

<div class="clip__content">
...
<canvas class="clip-canvas" canvas-id="clip-canvas"></canvas>
</div>

逻辑的话目前这个例子是使用了网络的 url 图片 所以要进行 download,如果是不用网络图片,那么这一句可以删除换成其他的获取图片 api

initCanvas() {
uni.showLoading({
title: "加载中...",
});
uni
.createSelectorQuery()
.select(".clip__content")
.fields(
{
size: true,
scrollOffset: true,
rect: true,
context: true,
computedStyle: ["transform", "translateX"],
scrollOffset: true,
},
(data) => {
uni.downloadFile({
url: this.imageUrl,
success: (res) => {
this.canvasInstance = uni.createCanvasContext(
"clip-canvas",
this
);
this.canvasInstance.drawImage(
res.tempFilePath,
-data.left,
-data.top,
this.systemInfo.screenWidth,
this.screenHeight,
0,
0
);
this.canvasInstance.draw(
false,
(() => {
setTimeout(() => {
uni.canvasToTempFilePath(
{
x: 0,
y: 0,
width: data.width,
height: data.height,
dWidth: data.width,
dHeight: data.height,
fileType: "jpg",
canvasId: "clip-canvas",
success: (data) => {
uni.hideLoading(); this.url = data.tempFilePath;
// this.canvasInstance.save();
},
},
this
);
}, 500);
})()
);
},
});
}
)
.exec();
},

效果如图所示:

本文转载于:

https://juejin.cn/post/6971977095652048910

如果对您有所帮助,欢迎您点个关注,我会定时更新技术文档,大家一起讨论学习,一起进步。

记录--uni-app实现京东canvas拍照识图功能的更多相关文章

  1. 调用本地摄像头并通过canvas拍照

    首先我们需要新建一个video标签,并且放到html里边 var video = document.createElement("video"); video.autoplay=& ...

  2. uni app中使用自定义图标库

    项目中难免会用到自定义图标,那在uni app中应该怎么使用呢? 首先, 将图标目录放在static资源目录下: 在main.js中引入就可以全局使用了 import '@/static/icon-o ...

  3. HTML5拍照、摄像机功能实战

    HTML5拍照.摄像机功能实战 苏格团队 作者:Tomey 开篇 最近在做一个chrome app的云相机应用,应用包括拍照.摄像.保存照片视频.上传文件等等核心功能,其中涉及到很多HTML5对媒体流 ...

  4. 安卓开发 利用百度识图api进行物体识别

    前文 之前的随笔中,已经通过相机或相册获取到了我们想要的图片,接下来进行识图api的配置工作.我使用的是百度的api,利用python获取信息,并在MainActivity中进行调用来输出信息. 一. ...

  5. 用canvas绘制折线图

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  6. iOS设计 - 一款APP从设计稿到切图过程概述

    这篇文章站在GUI设计师的角度概述了APP从项目启动到切片输出的过程,相当于工作流程的介绍.这里写的不是一种规范,只是一种工作方法,加上技术的更新是非常快的,大家在具体工作中,一定要灵活运用. 这里我 ...

  7. 使用Canvas绘制背景图

    原文  http://www.imququ.com/post/use-canvas-as-background-image.html 最近iCloud Web的Beta版换了UI,整体风格变得和iOS ...

  8. 实战使用Axure设计App,使用WebStorm开发(5) – 实现页面功能

    系列文章 实战使用Axure设计App,使用WebStorm开发(1) – 用Axure描述需求  实战使用Axure设计App,使用WebStorm开发(2) – 创建 Ionic 项目   实战使 ...

  9. OpenResy+Lua 利用百度识图 将图片地址解析成文字

    LUA代码:(注:LUA里有一个调用百度识图的接口IP:123.125.115.189(stu.baidu.com),不知为什么我的虚拟机无法解析stu.baidu.com,所以我只能PING出IP来 ...

  10. 一款APP从设计稿到切图过程全方位揭秘 Mark

    纯干货!一款APP从设计稿到切图过程全方位揭秘   @BAT_LCK:我本身是一名GUI设计师,所以我只站在GUI设计师的角度去把APP从项目启动到切片输出的过程写一写,相当于工作流程的介绍吧.公司不 ...

随机推荐

  1. Argocd学习

    argocd官网文档链接 ArgoCD官网文档 在K8S集群使用argocd命令将集群添加到argcd的cluster列表中 argocd cluster add kubernetes-admin@i ...

  2. SAM题目合集

    一些SAM的 基础 题目.(主要是我不想写SAM的原理啊啊啊) 有的题目是SA的思维题,但是可以用SAM平推,基本上可以不动脑子. 除非有特殊说明,否则将字符集看作所有小写字母,构造SAM复杂度记为 ...

  3. look命令

    look命令 look命令用于查询单词,仅需指定欲查询的字首字符串,它会显示所有开头字符串符合该条件的单词. 语法 look [-bdf] [-t char] string [file ...] 参数 ...

  4. ORACLE FORALL介绍

    ORACLE 10G OFFICIAL DOCUMNET  ---------------------------------------------------------------------- ...

  5. pikachu sql inject delete 注入

    留言板输入几条信息 出现删除按钮,点他 通过burpsuite拦截请求,请求报文如下 GET /vul/sqli/sqli_del.php?id=57 HTTP/1.1 Host: 192.168.1 ...

  6. ubuntu16.0.4设置mysql远程访问

    修改mysql的配置 1.先查看是不是已经是root用户了,不是的话切换到root用户 输入命令:sudo su 提示输入密码,这边输入你自己原来账户的密码即可 2.切换到root用户后,输入如下命令 ...

  7. 【算法day5】排序算法的稳定性及其汇总

    稳定性 定义:值相同的元素在排序完成之后能否保证原有的次序不变(同样值的个体之间,如果不因为排序而改变相对次序,这个排序就是有稳定性的,否则就没有) 举例: [2,1,3,1]-->[1,1,2 ...

  8. 使用Xilinx MIG验证硬件DDR设计

    1     导读 MIG 是xilinx的memory控制器,功能强大,接口易用.当硬件设计在设计对应的DDR接口时,最好先用MIG去配置一遍DDR的管脚约束.电平约束,从而避免硬件设计好了,实际却无 ...

  9. 【Azure Service Bus】使用Spring Cloud integration示例代码,为多个 Service Bus的连接使用 ConnectionString 方式

    问题描述 查看Service Bus的Java示例代码,发现使用Spring Cloud Integration,配置 Application.yaml 可以连接到两个Service Bus. 但代码 ...

  10. 【Azure 应用服务】Azure Function HTTP Trigger 遇见奇妙的500 Internal Server Error: Failed to forward request to http://169.254.130.x

    问题描述 使用 Azure Funciton App,在本地运行完全成功的Python代码,发布到Azure Function就出现了500  Internal Server Error. 而且错误消 ...