移动端svg移动缩放组件

注意:
1、组件支持横屏,此横屏时手机的自适应横屏,是竖屏顺时针旋转90度的横屏;
2、组件支持设置svg触摸开始时的回调方法;
3、组件支持设置svg缩放比例变化时的回调方法;

/**
 * @author: yiwenli
 * @desc: svg移动缩放
 * @date: 2023/8/15
 */
class EwlPanZoom {
	/**
	 * 构造器
	 *
	 * @param {Element} svgElement    svg元素
	 * @param {Object}  panZoomConfig 移动缩放配置
	 */
	constructor(svgElement, panZoomConfig = {}) {
		// 1、属性和变量初始化
		// svg元素
		this.svgElement = svgElement;
		// viewBox初始信息
		const { width, height } = this.svgElement.viewBox.animVal;
		this.viewBoxInitWidth = parseFloat(width);
		this.viewBoxInitHeight = parseFloat(height);
		// viewBox当前信息
		this.viewBoxCurrentX = '';
		this.viewBoxCurrentY = '';
		this.viewBoxCurrentWidth = '';
		this.viewBoxCurrentHeight = '';
		// 是否是移动状态
		this.isMoveStatus = false;
		// 移动开始坐标
		this.startX = '';
		this.startY = '';
		// 缩放开始时两手指的距离
		this.startDistance = '';
		// 是否横屏 横屏认为是顺时针旋转90度(默认非横屏)
		this.isHorizontal = panZoomConfig.isHorizontal || false;

		// 2、添加回调函数属性
		// 缩放倍数修改回调函数
		this.onZoomChange = panZoomConfig.onZoomChange;
		// 触摸开始回调函数
		this.onTouchStart = panZoomConfig.onTouchStart;

		// 3、执行初始化
		this.init();
	}
	/**
	 * 初始化
	 */
	init() {
		this.svgElement.addEventListener('touchstart', this.touchStart.bind(this), {
			passive: false
		});
		this.svgElement.addEventListener('touchmove', this.touchMove.bind(this), {
			passive: false
		});
	}
	/**
	 * 触摸开始事件 
	 *
	 * @param {Event} evt 事件对象
	 */
	touchStart(evt) {
		// 阻止浏览器的默认行为
		evt.preventDefault();

		if (typeof this.onTouchStart === 'function') {
			// 调用回调函数
			this.onTouchStart();
		}

		// 获取当前viewBox位置信息
		const viewBox = this.svgElement.viewBox.animVal;
		this.viewBoxCurrentX = parseFloat(viewBox.x);
		this.viewBoxCurrentY = parseFloat(viewBox.y);
		this.viewBoxCurrentWidth = parseFloat(viewBox.width);
		this.viewBoxCurrentHeight = parseFloat(viewBox.height);

		// 记录手指按下的位置
		if (this.isHorizontal) {
			this.startX = evt.touches[0].clientY;
			this.startY = this.viewBoxCurrentHeight - evt.touches[0].clientX;
		} else {
			this.startX = evt.touches[0].clientX;
			this.startY = evt.touches[0].clientY;
		}

		// 标记为正在移动
		this.isMoveStatus = true;

		if (evt.touches.length === 2) {
			// 双指
			// 计算移动前两手指之间的距离,勾股定理
			const start = evt.touches;
			const startDistanceX = start[0].clientX - start[1].clientX;
			const startDistanceY = start[0].clientY - start[1].clientY;
			this.startDistance = Math.sqrt(startDistanceX ** 2 + startDistanceY ** 2);
		}
	}
	/**
	 * 触摸移动事件
	 * 
	 * @param {Event} evt 事件对象
	 */
	touchMove(evt) {
		if (evt.touches.length === 1) {
			// 单指触摸

			if (this.isMoveStatus) {
				// 获取当前视窗宽度高度
				const screenWidth = document.body.clientWidth;
				const screenHeight = document.body.clientHeight;

				// 计算分辨率
				const productX = (this.viewBoxCurrentWidth / screenWidth).toFixed(2);
				const productY = (this.viewBoxCurrentHeight / screenHeight).toFixed(2);

				// 获取当前手指位置
				let endX, endY;
				if (this.isHorizontal) {
					endX = evt.touches[0].clientY;
					endY = this.viewBoxCurrentHeight - evt.touches[0].clientX;
				} else {
					endX = evt.touches[0].clientX;
					endY = evt.touches[0].clientY;
				}

				// 计算移动差值
				const moveX = (endX - this.startX);
				const moveY = (endY - this.startY);

				// 计算viewBox移动后的位置
				const viewBoxMoveX = this.viewBoxCurrentX - (moveX * productX);
				const viewBoxMoveY = this.viewBoxCurrentY - (moveY * productX);

				// 修改viewBox属性
				this.svgElement.setAttribute('viewBox',
					`${viewBoxMoveX} ${viewBoxMoveY } ${this.viewBoxCurrentWidth} ${this.viewBoxCurrentHeight}`);
			}
		} else if (evt.touches.length === 2) {
			// 双指触摸

			// 防止触发单指触摸事件
			this.isMoveStatus = false;

			// 计算移动后两手指之间的距离,勾股定理
			const end = evt.touches;
			const endDistanceX = end[0].clientX - end[1].clientX;
			const endDistanceY = end[0].clientY - end[1].clientY;
			const endDistance = Math.sqrt(endDistanceX ** 2 + endDistanceY ** 2);

			// 计算移动前后距离比值,即为放大倍数
			const zoom = endDistance / this.startDistance;

			// 计算viewBox缩放后的宽高
			const viewBoxScaleWidth = this.viewBoxCurrentWidth / zoom;
			const viewBoxScaleHeight = this.viewBoxCurrentHeight / zoom;

			// 计算viewBox偏移量
			const offSetX = (this.viewBoxCurrentWidth - viewBoxScaleWidth) / 2.0;
			const offSetY = (this.viewBoxCurrentHeight - viewBoxScaleHeight) / 2.0;

			// 修改viewBox属性
			this.svgElement.setAttribute('viewBox',
				`${parseFloat(this.viewBoxCurrentX + offSetX)} ${parseFloat(this.viewBoxCurrentY + offSetY)} ${viewBoxScaleWidth} ${viewBoxScaleHeight}`
				);

			// 相对于初始时的缩放比例
			const viewBoxZoom = this.viewBoxInitWidth / viewBoxScaleWidth;
			if (typeof this.onZoomChange === 'function') {
				// 调用回调函数传递当前缩放倍数
				this.onZoomChange(viewBoxZoom);
			}
		}
	}
}

export default EwlPanZoom
  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值