HarmonyOS三方开源组件——鸿蒙JS实现仿蚂蚁森林
实现的效果图:::: hljs-center
:::
分析实现过程:
1、接收外部传递给组件的一个数组(小球能量列表),及收集能量动画结束的位置
<!-- waterFlake.js -->
<p>props: {</p>
<p>//后台返回的小球信息</p>
<space09da727f35d31814bff360fe6a495423Code 0>
<h5>2、根据小球的数量,生成小球的随机位置坐标。</h5>
<pre><code> // 生成小球的x坐标数组
<p>let xRandom = this.randomCommon(1, 8, this.ballList.length)</p>
<p>let all_x = xRandom.map(item => {</p>
<p>return item <em> width </em> 0.10</p>
<p>});</p>
<p>//生成小球的y坐标数组</p>
<p>let yRandom = this.randomCommon(1, 8, this.ballList.length);</p>
<p>let all_y = yRandom.map(item => {</p>
<p>return item * height * 0.08</p>
})/**
<p> <em> 随机指定范围内N个不重复的数</p>
<p> </em> 最简单最基本的方法</p>
<p> *</p>
<p> <em> @param min 指定范围最小值</p>
<p> </em> @param max 指定范围最大值</p>
<p>* @param n 随机数个数</p>
<p> <em> @return 随机数列表</p>
<p> </em>/</p>
<p>randomCommon(min, max, n) {</p>
<space09da727f35d31814bff360fe6a495423Code 1>
3、根据传递进来的能量列表及生成的小球坐标,组装成我们需要的小球数据列表ballDataList[]
/**
<p> <em> ballDataList的每个对象包括以下属性:</p>
<p> </em> content(小球显示的文本信息)</p>
<p>* x(横坐标)、</p>
<p> <em> y(纵坐标)</p>
<p> </em>/</p>
ballDataList: [],let dataList = []
<p>for (let index = 0; index < this.ballList.length; index++) {</p>
<p>dataList.push({</p>
<space09da727f35d31814bff360fe6a495423Code 2>
<p> }</p>
this.ballDataList = dataList; // 触发视图更新4、绘制小球随机显示界面
<!-- waterFlake.hml -->
<p><div class="main_contain" ref="main_contain" id="main_contain"></p>
<p><text for="{{ ballDataList }}"</p>
<space09da727f35d31814bff360fe6a495423Code 3>
</div>.main_contain {
<p>width: 100%;</p>
<p>position: relative;</p>
<p>}
</p>
<p>.ball {</p>
<p>width: 120px;</p>
<p>height: 120px;</p>
<p>background-color: #c3f593;</p>
<p>background-size: 100%;</p>
<p>border-radius: 60px;</p>
<p>border: #69c78e;</p>
<p>border-bottom-style: solid;</p>
<p>border-width: 1px;</p>
<p>position: absolute;</p>
<p>text-align: center;</p>
}5、给小球添加动画:
由于鸿蒙JSUI框架@keyframes 动画只能指定动画初始样式(from属性)和终止样式(to属性),故只能采用JS给小球指定动画。
小球移动轨迹为上下浮动的简单动画,可有两种思路实现:
方式一:为每个小球设置连续无限次数动画
createShakeAnimate(el) {
<space09da727f35d31814bff360fe6a495423Code 4>
<space09da727f35d31814bff360fe6a495423Code 5>
<p>方式二:每个小球设置为单向动画,只执行一次,监听动画结束时,调用reverse()方法执行反转动画</p>
<pre><code>createShakeAnimate(el) {
<space09da727f35d31814bff360fe6a495423Code 6>
}执行浮动动画
<!-- waterFlake.hml 为每个小球指定id -->
<p><text for="{{ ballDataList }}"</p>
<p>class="ball"</p>
<p>id="ball{{ $idx }}"</p>
<p></p>
<space09da727f35d31814bff360fe6a495423Code 3><!-- waterFlake.js执行动画 -->
<p>playShakeAnimate() {</p>
<p>setTimeout(() => {</p>
<space09da727f35d31814bff360fe6a495423Code 8>
<h5>6、为小球设置点击事件及收集能量动画</h5>
<pre><code>onBallClick(index, item) {
<p>// 发送事件给父组件 并将小球信息作为参数传递出去</p>
<p>this.$emit('ballClick', item);</p>
<p>let el = this.$element(<code>ball${index})
this.playCollectionAnimate(el, index)
},/**
<p> <em> 执行收集的动画</p>
<p> </em> @param el</p>
<p>* @param index</p>
<p> <em> @return</p>
<p> </em>/</p>
<p>playCollectionAnimate(el, index) {</p>
<p>if (this.isCollect) { // 正在执行收集动画则直接return</p>
<space09da727f35d31814bff360fe6a495423Code 9>
<space09da727f35d31814bff360fe6a495423Code 10>
},7、父组件点击重置时,更新界面
onInit() {
<space09da727f35d31814bff360fe6a495423Code 11>
<p>},</p>
<p>onBallListChange(newV) { // 外部数据发生变化 重新渲染组件</p>
<space09da727f35d31814bff360fe6a495423Code 12>
<h3>完整代码如下:</h3>
<h4>子组件:</h4>
<pre><code><!-- waterFlake.css -->
<p>.main_contain {</p>
<p>width: 100%;</p>
<p>position: relative;</p>
<p>}
</p>
<p>.ball {</p>
<p>width: 100px;</p>
<p>height: 100px;</p>
<p>background-color: #c3f593;</p>
<p>background-size: 100%;</p>
<p>border-radius: 60px;</p>
<p>border: #69c78e;</p>
<p>border-bottom-style: solid;</p>
<p>border-width: 1px;</p>
<p>position: absolute;</p>
<p>text-align: center;</p>
<p>}
</p>
<p>@keyframes Wave {</p>
<p>from {</p>
<space09da727f35d31814bff360fe6a495423Code 13>
<p>to {</p>
<space09da727f35d31814bff360fe6a495423Code 14>
}<!-- waterFlake.hml -->
<p><div class="main_contain" ref="main_contain" id="main_contain"></p>
<p><text for="{{ ballDataList }}"</p>
<p>ref="ball{{ $idx }}" class="ball"</p>
<p>id="ball{{ $idx }}"</p>
<p>tid="ball{{ $idx }}"</p>
<p></p>
<space09da727f35d31814bff360fe6a495423Code 3>
</div><!-- waterFlake.js -->
<p>export default {</p>
<p>props: {</p>
<p>//后台返回的小球信息</p>
<p>ballList: {</p>
<p>default: ,</p>
<p>},</p>
<p>// 收集能量动画结束的X坐标</p>
<p>collDestinationX: {</p>
<p>default: 0</p>
<p>},</p>
<p>// 收集能量动画结束的Y坐标</p>
<p>collDestinationY: {</p>
<p>default: 600</p>
<p> }</p>
<p>},</p>
<p>data() {</p>
<p>return {</p>
<p>/**</p>
<p> <em> ballDataList的每个对象包括以下属性:</p>
<p> </em> content(小球显示的文本信息)</p>
<p>* x(横坐标)、</p>
<p> <em> y(纵坐标)、</p>
<p> </em>/</p>
<p>ballDataList: [],</p>
<p>isCollect: false // 是否正在执行收集能量动画</p>
<p>};</p>
<p>},</p>
<p>onInit() {</p>
<space09da727f35d31814bff360fe6a495423Code 11>
<p>},</p>
<p>onReady() {</p>
<p>let width = 720 //组件的款第</p>
<p>let height = 600 //组件的高度</p>
<p>// 生成小球的x坐标数组</p>
<p>let xRandom = this.randomCommon(1, 8, this.ballList.length)</p>
<p>let all_x = xRandom.map(item => {</p>
<p>return item * width <em> 0.10</p>
<p>});</p>
<p>//生成小球的y坐标数组</p>
<p>let yRandom = this.randomCommon(1, 8, this.ballList.length);</p>
<p>let all_y = yRandom.map(item => {</p>
<p>return item </em> height * 0.08</p>
<p>})</p>
<p>if (xRandom == null || yRandom == null) {</p>
<p>return</p>
<p> }</p>
<p>let dataList = []</p>
<p>for (let index = 0; index < this.ballList.length; index++) {</p>
<p>dataList.push({</p>
<p>content: this.ballList + 'g',</p>
<p>x: all_x,</p>
<p>y: all_y</p>
<p>})</p>
<p> }</p>
<p>this.ballDataList = dataList; // 触发视图更新</p>
<p>console.info('onReady ballDataList = ' + JSON.stringify(this.ballDataList));</p>
<space09da727f35d31814bff360fe6a495423Code 17>
<p>/**</p>
<p> <em> 执行收集的动画</p>
<p> </em> @param el</p>
<p>* @param index</p>
<p> <em> @return</p>
<p> </em>/</p>
<p>playCollectionAnimate(el, index) {</p>
<space09da727f35d31814bff360fe6a495423Code 18>
<p>
<space09da727f35d31814bff360fe6a495423Code 19>
</p>
<space09da727f35d31814bff360fe6a495423Code 20>
<p>/**</p>
<p> <em> 随机指定范围内N个不重复的数</p>
<p> </em> 最简单最基本的方法</p>
<p> *</p>
<p> <em> @param min 指定范围最小值</p>
<p> </em> @param max 指定范围最大值</p>
<p>* @param n 随机数个数</p>
<p> <em> @return 随机数列表</p>
<p> </em>/</p>
<p>randomCommon(min, max, n) {</p>
<space09da727f35d31814bff360fe6a495423Code 1>
<p>onBallListChange(newV) { // 外部数据发生变化 重新渲染组件</p>
<p>console.log('onBallListChange newV = ' + JSON.stringify(newV))</p>
<p>this.onReady()</p>
<p> }</p>
}父组件:
<!-- index.css -->
<p>.container {</p>
<p>flex-direction: column;</p>
<p>align-items: flex-start;</p>
<p>}
</p>
<p>.title {</p>
<p>font-size: 100px;</p>
<p>}
</p>
<p>.forestContainer {</p>
<p>width: 100%;</p>
<p>height: 750px;</p>
<p>background-image: url("/common/bg.jpg");</p>
<p>background-size: 100%;</p>
<p>background-repeat: no-repeat;</p>
}<!-- index.hml -->
<p><element name='waterFlake' src='../../../default/common/component/waterflake/waterFlake.hml'></element></p>
<p><div class="container"></p>
<p><div class="forestContainer"></p>
<space09da727f35d31814bff360fe6a495423Code 22>
</div><!-- index.js -->
<p>import prompt from '@system.prompt';</p>
<p>export default {</p>
<p>data() {</p>
<space09da727f35d31814bff360fe6a495423Code 23>
}gitee地址:
https://gitee.com/chinasoft4_ohos/CustomWaterView
作者:熊文功
想了解更多关于鸿蒙的内容,请访问:
51CTO和华为官方战略合作共建的鸿蒙技术社区
https://harmonyos.51cto.com/#bkwz
文档来源:51CTO技术博客https://blog.51cto.com/harmonyos/3253890
页:
[1]