1000字范文,内容丰富有趣,学习的好帮手!
1000字范文 > 【canvas教程】实现画布拖动 定点缩放 支持手势与鼠标滚轮操作

【canvas教程】实现画布拖动 定点缩放 支持手势与鼠标滚轮操作

时间:2018-07-02 22:56:30

相关推荐

【canvas教程】实现画布拖动 定点缩放 支持手势与鼠标滚轮操作

效果展示:

实现原理:

1.求实现定点缩放所需的位移值

定点缩放:从图中选取某点(参照物)为中心点进行缩放,缩放时无论图像怎么变化,该点位置始终固定不变。

有想深入了解实现原理的,不妨试着做一下我下面出的一道题:

有一个长4000px、宽4000px的四方形ABCD,A点的坐标固定在(-2000,-2000),该四边形上有一个点E,坐标为(-100,-300),将该四方形复制一份并缩小到90%后,新四边形的A点坐标为多少时可使新四边形的E点与原四边形的E点重合?

2.求手势缩放的中心点

勾股定理:直角三角形两直角边为a和b,斜边为c,那么a²+b²=c²。

根据勾股定理求得两点间距离后,再求得两点间的中点坐标。

中点坐标 = (起点坐标) + (两点间距 / 2) = (终点坐标) - (两点间距 / 2)

实现代码:

<template><view><canvasid="gameCanvas"canvas-id="gameCanvas"disable-scrollstyle="width: 100vw;height: 100vh;"@touchstart="onTouchStart"@touchmove="onTouchMove"></canvas></view></template>

<script>import img from '@/static/images/demo.jpg';export default {canvasContext: null,data() {return {/** 最小缩放百分比 */minScale: 30,/** 最大缩放百分比 */maxScale: 100,/** 单次缩放百分比增量 */increaseScale: 5,/** 缩放比例 */scale: 1,touchType: '',start: {x: 0,y: 0},touches: [],change: {x: 0,y: 0},/** 是否正在绘制 */drawing: false,// 视图宽度viewWidth: 0,// 视图高度viewHeight: 0,// 画布绘制的图像宽度width: 4000,// 画布绘制的图像高度height: 4000}},onReady() {const sys = uni.getSystemInfoSync();this.viewWidth = sys.screenWidth;this.viewHeight = sys.screenHeight;this.canvasContext = uni.createCanvasContext('gameCanvas');this.drawImg();},methods: {drawImg() {// 拦截频繁绘制if(this.drawing) return;this.drawing = true;var ctx = this.canvasContext;// 改变坐标系原点ctx.translate(this.change.x, this.change.y);// 绘制图片ctx.drawImage(img, 0, 0, this.width * this.scale, this.height * this.scale);ctx.draw(false, () => {this.drawing = false});},/*** 限制 x、y 拖动范围,禁止滑出边界* @param {number} x* @param {number} y*/checkRange(point) {point.x = Math.min(Math.max(point.x, -this.width * this.scale + this.viewWidth), 0);point.y = Math.min(Math.max(point.y, -this.height * this.scale + this.viewHeight), 0);return point;},/*** 缩放画布* @param {boolean} reduce 是否缩小,否则为放大*/scaleCanvas(reduce, e) {let last = this.scale;let scale = Math.round(last * 100); // 转为整数计算,无精度丢失问题if(reduce) { // 缩小 / 鼠标滚轮向下滚动scale = scale > this.minScale ? scale - this.increaseScale : this.minScale;} else { // 放大 / 鼠标滚轮向上滚动scale = scale < this.maxScale ? scale + this.increaseScale : this.maxScale;}if(last !== scale / 100) {this.scale = scale / 100;this.start = { x: 0, y: 0 };// 参考问题:有一个长4000px、宽4000px的四方形ABCD,A点的坐标固定在(-2000,-2000),// 该四边形上有一个点E,坐标为(-100,-300),将该四方形复制一份并缩小到90%后,// 新四边形的A点坐标为多少时可使新四边形的E点与原四边形的E点重合?// 预期效果:从图中选取某点(参照物)为中心点进行缩放,缩放时无论图像怎么变化,该点位置始终固定不变// 计算方法:以相同起点先计算缩放前后两点间的距离,再加上原图像偏移量即可this.movingCanvas({isScaled: true,pageX: (e.pageX - this.change.x) * (1 - this.scale / last),pageY: (e.pageY - this.change.y) * (1 - this.scale / last),});}},onTouchStart(e) {this.start.x = e.touches[0].pageX;this.start.y = e.touches[0].pageY;this.touchType = '';},/*** 计算两点间距* @param {Object} touches */getDistanceByTouches(touches) {// 根据勾股定理求两点间距离const a = touches[1].pageX - touches[0].pageX;const b = touches[1].pageY - touches[0].pageY;const c = Math.sqrt(Math.pow(a, 2) + Math.pow(b, 2));// 求两点间的中点坐标// 1. a、b可能为负值// 2. 在求a、b时,如用touches[1]减touches[0],则求中点坐标也得用touches[1]减a/2、b/2// 3. 同理,在求a、b时,也可用touches[0]减touches[1],则求中点坐标也得用touches[0]减a/2、b/2const x = touches[1].pageX - a / 2;const y = touches[1].pageY - b / 2;// return { a: Math.abs(a), b: Math.abs(b), c, x, y };return { c, x, y };},onTouchMove(e) {if(e.touches.length === 2) { // 双点触摸 / 缩放if(this.touchType === 'scale') {const start = this.getDistanceByTouches(this.touches);const end = this.getDistanceByTouches(e.touches);if(Math.abs(start.c - end.c) >= 10) { // 滑动多长距离触发缩放this.touches = e.touches;this.scaleCanvas(start.c > end.c, // 两点间距变小=缩小{pageX: end.x,pageY: end.y});}} else {this.touches = e.touches;}this.touchType = 'scale';} else {if(this.touchType === 'scale') {// 从双点触摸变成单点触摸 / 从缩放变成拖动this.start.x = e.touches[0].pageX;this.start.y = e.touches[0].pageY;this.touchType = 'move';return;}this.movingCanvas(e.touches[0])this.touchType = 'move';}},movingCanvas(touche) {let point = {x: this.change.x + touche.pageX - this.start.x,y: this.change.y + touche.pageY - this.start.y};this.checkRange(point);if(touche.isScaled || this.change.x !== point.x || this.change.y !== point.y) {this.change = point;this.drawImg();this.start.x = touche.pageX;this.start.y = touche.pageY;}}}}</script>

监听Web端鼠标滚轮实现定点缩放

加入 mousewheel 监听鼠标滚轮事件后,在回调中直接调用 scaleCanvas 即可。

onReady() {const sys = uni.getSystemInfoSync();this.viewWidth = sys.screenWidth;this.viewHeight = sys.screenHeight;this.canvasContext = uni.createCanvasContext('gameCanvas');this.drawImg();// 监听 PC 端鼠标滚轮if(sys.uniPlatform === 'web') {window.addEventListener('mousewheel', (e) => {this.scaleCanvas(e.deltaY > 0, e);});}},

其他说明:

1.如使用 ts 语法,在 import 图片时遇 `Cannot find module...` 错误,可在 `*.d.ts` 文件中添加 `declare module '*.jpg';`

2.示例代码是基于uniapp 框架实现。

3.其中画布拖动的代码是基于我上篇文章【canvas教程】绘制大图并实现画布拖动,如只实现拖动或区分拖动和缩放的代码可参考上篇文章。

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。