文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

HarmonyOS - 基于ArkUI(JS)实现虚拟摇杆组件

2024-12-01 13:55

关注

​想了解更多关于开源的内容,请访问:​

​51CTO 开源基础软件社区​

​https://ost.51cto.com​​。

前言

虚拟摇杆在移动端游戏中是最常见看的,用来实现游戏中的精灵移动。本案例中使用jspai中的div和image组件来实现的虚拟摇杆组件,然后监听touch事件获取滑动的方向和位置x,y。

开发环境说明

展示效果

属性

属性名

类型

默认值

作用

rocker-data

Object

-

配置摇杆的参数,参考下面​​rockerData​

rockerData

属性名

类型

默认值

作用

ou_width

Number

140

摇杆外圆宽度

ou_height

Number

140

摇杆外圆高度

in_width

Number

60

摇杆内圆宽度

in_height

Number

60

摇杆内圆高度

ou_img

Image

-

摇杆外圆图片

in_img

Image

-

摇杆内圆图片

组件事件

属性名

类型

返回值

备注

play

Function

{x:Number, y:Number, angle:Number}

x: 摇杆滑动的x, y: 摇杆滑动的y, angle: 对应x方向的角度

调用实现

hml部分:

<element name="yg-rocker" src="../../common/component/ygRocker.hml">element>
<div class="container" ref="box">
<yg-rocker
rocker-data="{{rockerData}}"
@play="play"
>yg-rocker>
div>

js部分:

import Log from '../../common/utils/log.js'
const log = new Log('index.js页面')
export default {
data: {
rockerData: {
ou_width: 140,
ou_height: 140,
in_width: 60,
in_height: 60,
ou_img: '/common/images/rocker_bg.png',
in_img: '/common/images/rocker.png',
},
d_x: 0,
d_y: 0,
window: {
w: 720,
h: 332
},
angle: 0
},
onInit() {
},
onShow(){
let d = this.$refs.box.getBoundingClientRect();
this.window.w = d.width || 720;
this.window.h = d.height || 332;
},
play(e){
let opt = e.detail
let {x, y, angle} = opt;
this.angle = angle;
this.d_x = x;
this.d_y = y;
}
}

实现过程

1、首先渲染虚拟摇杆的外圆和内圆

通过css调整:

.yg-rocker{
position: fixed;
bottom: 40px;
left: 40px;
}
.yg-rocker{
opacity: .4;
}
.yg-rocker-bg .active-bg{
box-shadow: 0fp 0 10px 5px rgba(0,170,255,.2);
opacity: .6;
}
.yg-rocker .yg-rocker-item{
position: absolute;
}

最后得到:

2、给虚拟摇杆添加touch事件

<div
class="yg-rocker-bg"
ref="ygRockerBg"
@touchstart="touchStart"
@touchmove="touchMove"
@touchend="touchEnd"
>

touchStart触摸开始事件:

touchStart(e){
let t = e.touches[0];
let d = this.$refs.ygRockerItem.getBoundingClientRect();
this.x = d.left + d.width / 2;
this.y = d.top + d.height / 2;
this.isTouch = true;
this.setSide(t);
this.ani(10);
},

触摸滑动事件和触摸结束事件。

// 触摸滑动事件也交给setSide方法处理
touchMove(e){
let t = e.touches[0];
this.setSide(t);
},
// 触摸结束,摇杆内圆回归到最开始位置
touchEnd(){
this.isTouch = false;
// 回到中心位置
this.top = 0;
this.left = 0;
},

3、对滑动的位置处理

setSide(t){
let x = this.x - t.globalX;
let y = this.y - t.globalY;
// 获取到当前位置到圆心半径
let temp = Math.sqrt(Math.pow(x,2) + Math.pow(y,2));
let r = this.rockerData.ou_width / 2;
let r2 = temp <= r ? r : temp;
let top = Math.sin(y/r2) * (this.rockerData.ou_width / 2);
let left = Math.sin(x/r2) * (this.rockerData.ou_width / 2);
this.top = this.setFlag(top);
this.left = this.setFlag(left);
this.xx = -1 * x * this.speed;
this.yy = -1 * y * this.speed;
this.angle = this.getAngle({x: (-1 * x), y});
},
setFlag(num){
return num > 0 ? 0 - num : Math.abs(num);
},

4、获取角度

获取当前手指和内圆圆心所在x轴方向的角度。用来判断物体的方向。
因为通过css的rotate来判断实现物体方向,所以以x轴方向为起点,顺时针为递增从0到360°
圆的周长为2Πr,也就是说2Π为圆的360°,一个Π就是180°,使用三角函数的反正切可求得当前位置对应圆心的角度。
但是因为是正切,所以取值只有0到90°或者是-0到-90°。
所以需要根据在象限的位置来计算内圆圆心为坐标原点,x轴为起边的顺时针角度。

getAngle(obj){
let {x, y} = obj;
//返回角度,不是弧度
let res = 180 * Math.atan(y / x) / Math.PI;
if(x > 0 && y > 0){
res = 90 - Math.abs(res)
}
if(x > 0 && y < 0){
res = 90 + Math.abs(res)
}
if(x < 0 && y < 0){
res = 180 + (90-Math.abs(res))
}
if(x < 0 && y > 0){
res = 270 + Math.abs(res)
}
return res === res ? res.toFixed(2) : 0;
}

5、动画帧处理

ani传入一个定时器的时间,表示这个时间段刷新一次动画。
因为我们触摸的时候,如果在一个方向触摸停止了,但是操作的物体不应该是停止的。而是根据这个方向继续根据当前速度前进。所以需要使用定时器操作刷新这个动画帧。

ani(t){
clearInterval(this.timer);
this.timer = setInterval(()=>{
if(!this.isTouch){
clearInterval(this.timer)
} else {
this.d_x = this.d_x + this.xx;
this.d_y = this.d_y + this.yy;
this.$emit('play', {x: this.d_x, y: this.d_y, angle: this.angle})
// 下面的操作都是为了防止物体(坦克)离开屏幕画面。
if(this.d_x <= 0){
this.d_x = 0;
}
if(this.d_x >= 680){
this.d_x = 680;
}
if(this.d_y <= 0){
this.d_y = 0;
}
if(this.d_y >= 292){
this.d_y = 292;
}
}
},t)
},

最后的效果就出来了。

6、最后,画一个坦克来验证虚拟摇杆的数据。

<div class="tank" style="transform: rotate({{angle}}deg); top: {{d_y}}px; left: {{d_x}}px;">
<div class="l1">div>
<div class="l2">div>
<div class="c">div>
<div class="g">div>
<div class="r">div>
div>

最后我们再次看一下效果

代码地址

https://gitee.com/yango520/yg-rocker。

总结

整体的实现就是这样,逻辑也比较简单,当然也有些bug,比如滑动的速度没有限制超出摇杆外圆的时候而限制。坦克用div画的,如果需要做更复杂的操作,需要使用canvas来作为画布场景。

​想了解更多关于开源的内容,请访问:​

​51CTO 开源基础软件社区​

​https://ost.51cto.com​​。

来源:51CTO开源基础软件社区内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯