自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

HarmonyOS三方開源組件—鴻蒙JS實現(xiàn)仿螞蟻森林

開源 OpenHarmony
HarmonyOS三方開源組件之鴻蒙JS實現(xiàn)仿螞蟻森林,自定義仿支付寶螞蟻森林水滴控件,提供了JAVA UI和JS UI兩種實現(xiàn)方式。

[[415355]]

想了解更多內(nèi)容,請訪問:

51CTO和華為官方合作共建的鴻蒙技術(shù)社區(qū)

https://harmonyos.51cto.com

實現(xiàn)的效果圖:

分析實現(xiàn)過程:

1、接收外部傳遞給組件的一個數(shù)組(小球能量列表),及收集能量動畫結(jié)束的位置

  1. <!-- waterFlake.js --> 
  2.  props: { 
  3.     //后臺返回的小球信息 
  4.         ballList: { 
  5.             default: [10, 11, 12, 13, 14], 
  6.         }, 
  7.     // 收集能量動畫結(jié)束的X坐標(biāo) 
  8.         collDestinationX: { 
  9.             default: 350 
  10.         }, 
  11.     // 收集能量動畫結(jié)束的Y坐標(biāo) 
  12.         collDestinationY: { 
  13.             default: 400 
  14.         } 
  15.     }, 

2、根據(jù)小球的數(shù)量,生成小球的隨機(jī)位置坐標(biāo)。

  1. // 生成小球的x坐標(biāo)數(shù)組 
  2.  let xRandom = this.randomCommon(1, 8, this.ballList.length) 
  3.  let all_x = xRandom.map(item => { 
  4.      return item * width * 0.10 
  5.  }); 
  6.  //生成小球的y坐標(biāo)數(shù)組 
  7.  let yRandom = this.randomCommon(1, 8, this.ballList.length); 
  8.  let all_y = yRandom.map(item => { 
  9.      return item * height * 0.08 
  10.  }) 
  1. /** 
  2.      * 隨機(jī)指定范圍內(nèi)N個不重復(fù)的數(shù) 
  3.      * 最簡單最基本的方法 
  4.      * 
  5.      * @param min 指定范圍最小值 
  6.      * @param max 指定范圍最大值 
  7.      * @param n   隨機(jī)數(shù)個數(shù) 
  8.      * @return 隨機(jī)數(shù)列表 
  9.      */ 
  10.     randomCommon(minmax, n) { 
  11.         if (n > (max - min + 1) || max < min) { 
  12.             return null
  13.         } 
  14.         let result = []; 
  15.         let count = 0; 
  16.         while (count < n) { 
  17.             let num = parseInt((Math.random() * (max - min)) + min); 
  18.             let flag = true
  19.             for (let j = 0; j < n; j++) { 
  20.                 if (num == result[j]) { 
  21.                     flag = false
  22.                     break; 
  23.                 } 
  24.             } 
  25.             if (flag) { 
  26.                 result[count] = num; 
  27.                 count++; 
  28.             } 
  29.         } 
  30.         return result; 
  31.     }, 

3、根據(jù)傳遞進(jìn)來的能量列表及生成的小球坐標(biāo),組裝成我們需要的小球數(shù)據(jù)列表ballDataList[]

  1. /** 
  2.  * ballDataList的每個對象包括以下屬性: 
  3.  * content(小球顯示的文本信息) 
  4.  * x(橫坐標(biāo))、 
  5.  * y(縱坐標(biāo)) 
  6.  */ 
  7. ballDataList: [], 
  1. let dataList = [] 
  2.  for (let index = 0; index < this.ballList.length; index++) { 
  3.      dataList.push({ 
  4.          content: this.ballList[index] + 'g'
  5.          x: all_x[index], 
  6.          y: all_y[index
  7.      }) 
  8.  } 
  9.  this.ballDataList = dataList; // 觸發(fā)視圖更新 

4、繪制小球隨機(jī)顯示界面

  1. <!-- waterFlake.hml --> 
  2. <div class="main_contain" ref="main_contain" id="main_contain"
  3.     <text for="{{ ballDataList }}" 
  4.           style="top : {{ $item.y }} px; 
  5.                   left : {{ $item.x }} px;" 
  6.             >{{ $item.content }}</text> 
  7.  
  8. </div> 
  1. .main_contain { 
  2.     width: 100%; 
  3.     position: relative
  4.  
  5. .ball { 
  6.     width: 120px; 
  7.     height: 120px; 
  8.     background-color: #c3f593; 
  9.     background-size: 100%; 
  10.     border-radius: 60px; 
  11.     border: #69c78e; 
  12.     border-bottom-style: solid; 
  13.     border-width: 1px; 
  14.     position: absolute
  15.     text-align: center; 

5、給小球添加動畫:

由于鴻蒙JSUI框架@keyframes 動畫只能指定動畫初始樣式(from屬性)和終止樣式(to屬性),故只能采用JS給小球指定動畫。

小球移動軌跡為上下浮動的簡單動畫,可有兩種思路實現(xiàn):

方式一:為每個小球設(shè)置連續(xù)無限次數(shù)動畫

  1. createShakeAnimate(el) { 
  2.         if (el == null || el == undefined) { 
  3.             return 
  4.         } 
  5.         var options = { 
  6.             duration: 2000, 
  7.             easing: 'friction'
  8.             fill: 'forwards'
  9.             iterations: "Infinity"
  10.         }; 
  11.         var frames = [ 
  12.             { 
  13.                 transform: { 
  14.                     translate: '0px 0px' 
  15.                 }, 
  16.                 offset: 0.0 // 動畫起始時 
  17.             }, 
  18.             { 
  19.                 transform: { 
  20.                     translate: '0px 20px' 
  21.                 }, 
  22.                 offset: 0.5 // 動畫執(zhí)行至一半時 
  23.             }, 
  24.             { 
  25.                 transform: { 
  26.                     translate: '0px 0px' 
  27.                 }, 
  28.                 offset: 1.0 // 動畫結(jié)束時 
  29.             }, 
  30.  
  31.         ]; 
  32.         let animation = el.animate(frames, options); 
  33.         return animation 
  34.     }, 

方式二:每個小球設(shè)置為單向動畫,只執(zhí)行一次,監(jiān)聽動畫結(jié)束時,調(diào)用reverse()方法執(zhí)行反轉(zhuǎn)動畫

  1. createShakeAnimate(el) { 
  2.       if (el == null || el == undefined) { 
  3.           return 
  4.       } 
  5.       var options = { 
  6.           duration: 2000, 
  7.           easing: 'friction'
  8.           fill: 'forwards'
  9.           iterations: 1, 
  10.       }; 
  11.       var frames = [ 
  12.           { 
  13.               transform: { 
  14.                   translate: '0px 0px' 
  15.               }, 
  16.               offset: 0.0 
  17.           }, 
  18.           { 
  19.               transform: { 
  20.                   translate: '0px 20px' 
  21.               }, 
  22.               offset: 1.0 
  23.           }, 
  24.       ]; 
  25.       let animation = el.animate(frames, options); 
  26.        animation.onfinish = function () { 
  27.           animation.reverse() 
  28.       }; 
  29.       return animation 

執(zhí)行浮動動畫

  1. <!-- waterFlake.hml 為每個小球指定id --> 
  2.   <text for="{{ ballDataList }}" 
  3.           class="ball" 
  4.           id="ball{{ $idx }}" 
  5.           onclick="onBallClick($idx,$item)" 
  6.           style="top : {{ $item.y }} px; 
  7.                   left : {{ $item.x }} px;" 
  8.             >{{ $item.content }}</text> 
  1. <!-- waterFlake.js  執(zhí)行動畫 --> 
  2.  playShakeAnimate() { 
  3.       setTimeout(() => { 
  4.           console.info('xwg playShakeAnimate '); 
  5.           for (var index = 0; index < this.ballDataList.length; index++) { 
  6.               let el = this.$element(`ball${index}`) 
  7.               let animate = this.createShakeAnimate(el) 
  8.               animate.play() 
  9.           } 
  10.       }, 50) 
  11.     }, 

6、為小球設(shè)置點擊事件及收集能量動畫

  1. onBallClick(index, item) { 
  2.    // 發(fā)送事件給父組件 并將小球信息作為參數(shù)傳遞出去 
  3.    this.$emit('ballClick', item); 
  4.  
  5.    let el = this.$element(`ball${index}`) 
  6.    this.playCollectionAnimate(el, index
  7. }, 
  1. /** 
  2.  * 執(zhí)行收集的動畫 
  3.  * @param el 
  4.  * @param index 
  5.  * @return 
  6.  */ 
  7. playCollectionAnimate(el, index) { 
  8.     if (this.isCollect) { // 正在執(zhí)行收集動畫則直接return 
  9.         return 
  10.     } 
  11.     var options = { 
  12.         duration: 1500, 
  13.         easing: 'ease-in-out'
  14.         fill: 'forwards'
  15.     }; 
  16.     let offsetX = this.collDestinationX - this.ballDataList[index].x 
  17.     let offsetY = this.collDestinationY - this.ballDataList[index].y 
  18.     var frames = [ 
  19.         { 
  20.             transform: { 
  21.                 translate: '0px 0px' 
  22.             }, 
  23.             opacity: 1 
  24.         }, 
  25.         { 
  26.             transform: { 
  27.                 translate: `${offsetX}px ${offsetY}px` 
  28.             }, 
  29.             opacity: 0 
  30.         } 
  31.     ]; 
  32.     let animation = el.animate(frames, options); 
  33.     let _t = this 
  34.     animation.onfinish = function () { 
  35.         console.info('onBallClick collection animation onFinish'); 
  36.         _t.isCollect = false
  37.         _t.ballDataList.splice(index, 1); 
  38.         console.info(JSON.stringify(_t.ballDataList)); 
  39.  
  40.         // 調(diào)用splice方法后,原index位置的小球不再執(zhí)行動畫,故手動再創(chuàng)建動畫 
  41.         if (index <= _t.ballDataList.length) { 
  42.             setTimeout(() => { 
  43.                 let animate = _t.createShakeAnimate(el) 
  44.                 animate.play() 
  45.             }, 5) 
  46.         } 
  47.     }; 
  48.     this.isCollect = true 
  49.     animation.play() 
  50. }, 

7、父組件點擊重置時,更新界面

  1.  onInit() { 
  2.         this.$watch('ballList''onBallListChange'); //注冊數(shù)據(jù)變化監(jiān)聽 
  3. }, 
  4. onBallListChange(newV) { // 外部數(shù)據(jù)發(fā)生變化 重新渲染組件 
  5.         console.log('onBallListChange newV = ' + JSON.stringify(newV)) 
  6.         this.onReady() 
  7.     } 

完整代碼如下:

子組件:

  1. <!-- waterFlake.css --> 
  2. .main_contain { 
  3.     width: 100%; 
  4.     position: relative
  5.  
  6. .ball { 
  7.     width: 100px; 
  8.     height: 100px; 
  9.     background-color: #c3f593; 
  10.     background-size: 100%; 
  11.     border-radius: 60px; 
  12.     border: #69c78e; 
  13.     border-bottom-style: solid; 
  14.     border-width: 1px; 
  15.     position: absolute
  16.     text-align: center; 
  17.  
  18. @keyframes Wave { 
  19.     from { 
  20.         transform: translateY(0px); 
  21.     } 
  22.  
  23.     to { 
  24.         transform: translateY(10px); 
  25.     } 
  1. <!-- waterFlake.hml --> 
  2. <div class="main_contain" ref="main_contain" id="main_contain"
  3.     <text for="{{ ballDataList }}" 
  4.           ref="ball{{ $idx }}" class="ball" 
  5.           id="ball{{ $idx }}" 
  6.           tid="ball{{ $idx }}" 
  7.           onclick="onBallClick($idx,$item)" 
  8.           style="top : {{ $item.y }} px; 
  9.                   left : {{ $item.x }} px;" 
  10.             >{{ $item.content }}</text> 
  11.  
  12. </div> 
  1. <!-- waterFlake.js --> 
  2. export default { 
  3.     props: { 
  4.     //后臺返回的小球信息 
  5.         ballList: { 
  6.             default: [10, 11, 12, 13, 14], 
  7.         }, 
  8.     // 收集能量動畫結(jié)束的X坐標(biāo) 
  9.         collDestinationX: { 
  10.             default: 0 
  11.         }, 
  12.     // 收集能量動畫結(jié)束的Y坐標(biāo) 
  13.         collDestinationY: { 
  14.             default: 600 
  15.         } 
  16.     }, 
  17.     data() { 
  18.         return { 
  19.         /** 
  20.              * ballDataList的每個對象包括以下屬性: 
  21.              * content(小球顯示的文本信息) 
  22.              * x(橫坐標(biāo))、 
  23.              * y(縱坐標(biāo))、 
  24.              */ 
  25.             ballDataList: [], 
  26.             isCollect: false // 是否正在執(zhí)行收集能量動畫 
  27.         }; 
  28.     }, 
  29.     onInit() { 
  30.         this.$watch('ballList''onBallListChange'); //注冊數(shù)據(jù)變化監(jiān)聽 
  31.     }, 
  32.     onReady() { 
  33.         let width = 720 //組件的款第 
  34.         let height = 600 //組件的高度 
  35.         // 生成小球的x坐標(biāo)數(shù)組 
  36.         let xRandom = this.randomCommon(1, 8, this.ballList.length) 
  37.         let all_x = xRandom.map(item => { 
  38.             return item * width * 0.10 
  39.         }); 
  40.         //生成小球的y坐標(biāo)數(shù)組 
  41.         let yRandom = this.randomCommon(1, 8, this.ballList.length); 
  42.         let all_y = yRandom.map(item => { 
  43.             return item * height * 0.08 
  44.         }) 
  45.         if (xRandom == null || yRandom == null) { 
  46.             return 
  47.         } 
  48.         let dataList = [] 
  49.         for (let index = 0; index < this.ballList.length; index++) { 
  50.             dataList.push({ 
  51.                 content: this.ballList[index] + 'g'
  52.                 x: all_x[index], 
  53.                 y: all_y[index
  54.             }) 
  55.         } 
  56.         this.ballDataList = dataList; // 觸發(fā)視圖更新 
  57.         console.info('onReady ballDataList = ' + JSON.stringify(this.ballDataList)); 
  58.  
  59.         this.playShakeAnimate() // 開始執(zhí)行抖動動畫 
  60.     }, 
  61.     onBallClick(index, item) { 
  62.         console.info('onBallClick index = ' + index); 
  63.         console.info('onBallClick item = ' + JSON.stringify(item)); 
  64.         this.$emit('ballClick', item); 
  65.         let el = this.$element(`ball${index}`) 
  66.         this.playCollectionAnimate(el, index
  67.     }, 
  68. /** 
  69.      * 執(zhí)行收集的動畫 
  70.      * @param el 
  71.      * @param index 
  72.      * @return 
  73.      */ 
  74.     playCollectionAnimate(el, index) { 
  75.         if (this.isCollect) { // 正在執(zhí)行收集動畫則直接return 
  76.             return 
  77.         } 
  78.         var options = { 
  79.             duration: 1500, 
  80.             easing: 'ease-in-out'
  81.             fill: 'forwards'
  82.         }; 
  83.         let offsetX = this.collDestinationX - this.ballDataList[index].x 
  84.         let offsetY = this.collDestinationY - this.ballDataList[index].y 
  85.         var frames = [ 
  86.             { 
  87.                 transform: { 
  88.                     translate: '0px 0px' 
  89.                 }, 
  90.                 opacity: 1 
  91.             }, 
  92.             { 
  93.                 transform: { 
  94.                     translate: `${offsetX}px ${offsetY}px` 
  95.                 }, 
  96.                 opacity: 0 
  97.             } 
  98.         ]; 
  99.         let animation = el.animate(frames, options); 
  100.         let _t = this 
  101.         animation.onfinish = function () { 
  102.             console.info('onBallClick collection animation onFinish'); 
  103.             _t.isCollect = false
  104.             _t.ballDataList.splice(index, 1); 
  105.             console.info(JSON.stringify(_t.ballDataList)); 
  106.  
  107.             // 調(diào)用splice方法后,原index位置的小球不再執(zhí)行動畫,故手動再創(chuàng)建動畫 
  108.             if (index <= _t.ballDataList.length) { 
  109.                 setTimeout(() => { 
  110.                     let animate = _t.createShakeAnimate(el) 
  111.                     animate.play() 
  112.                 }, 5) 
  113.             } 
  114.         }; 
  115.         this.isCollect = true 
  116.         animation.play() 
  117.     }, 
  118.     createShakeAnimate(el) { 
  119.         if (el == null || el == undefined) { 
  120.             return 
  121.         } 
  122.         var options = { 
  123.             duration: 2000, 
  124.             easing: 'friction'
  125.             fill: 'forwards'
  126.             iterations: "Infinity"
  127.         }; 
  128.         var frames = [ 
  129.             { 
  130.                 transform: { 
  131.                     translate: '0px 0px' 
  132.                 }, 
  133.                 offset: 0.0 
  134.             }, 
  135.             { 
  136.                 transform: { 
  137.                     translate: '0px 20px' 
  138.                 }, 
  139.                 offset: 0.5 
  140.             }, 
  141.             { 
  142.                 transform: { 
  143.                     translate: '0px 0px' 
  144.                 }, 
  145.                 offset: 1.0 
  146.             }, 
  147.  
  148.         ]; 
  149.         let animation = el.animate(frames, options); 
  150.         return animation 
  151.     }, 
  152.     playShakeAnimate() { 
  153.         setTimeout(() => { 
  154.             console.info('xwg playShakeAnimate '); 
  155.             for (var index = 0; index < this.ballDataList.length; index++) { 
  156.                 let el = this.$element(`ball${index}`) 
  157.                 let animate = this.createShakeAnimate(el) 
  158.                 animate.play() 
  159.             } 
  160.         }, 50) 
  161.     }, 
  162. /** 
  163.      * 隨機(jī)指定范圍內(nèi)N個不重復(fù)的數(shù) 
  164.      * 最簡單最基本的方法 
  165.      * 
  166.      * @param min 指定范圍最小值 
  167.      * @param max 指定范圍最大值 
  168.      * @param n   隨機(jī)數(shù)個數(shù) 
  169.      * @return 隨機(jī)數(shù)列表 
  170.      */ 
  171.     randomCommon(minmax, n) { 
  172.         if (n > (max - min + 1) || max < min) { 
  173.             return null
  174.         } 
  175.         let result = []; 
  176.         let count = 0; 
  177.         while (count < n) { 
  178.             let num = parseInt((Math.random() * (max - min)) + min); 
  179.             let flag = true
  180.             for (let j = 0; j < n; j++) { 
  181.                 if (num == result[j]) { 
  182.                     flag = false
  183.                     break; 
  184.                 } 
  185.             } 
  186.             if (flag) { 
  187.                 result[count] = num; 
  188.                 count++; 
  189.             } 
  190.         } 
  191.         return result; 
  192.     }, 
  193.     onBallListChange(newV) { // 外部數(shù)據(jù)發(fā)生變化 重新渲染組件 
  194.         console.log('onBallListChange newV = ' + JSON.stringify(newV)) 
  195.         this.onReady() 
  196.     } 

父組件:

  1. <!-- index.css --> 
  2. .container { 
  3.     flex-direction: column
  4.     align-items: flex-start; 
  5.  
  6. .title { 
  7.     font-size: 100px; 
  8.  
  9. .forestContainer { 
  10.     width: 100%; 
  11.     height: 750px; 
  12.     background-image: url("/common/bg.jpg"); 
  13.     background-size: 100%; 
  14.     background-repeat: no-repeat; 
  1. <!-- index.hml --> 
  2. <element name='waterFlake' src='../../../default/common/component/waterflake/waterFlake.hml'></element> 
  3. <div class="container"
  4.     <div class="forestContainer"
  5.         <waterFlake ball-list="{{ ballList }}" @ball-click="onBallClick"></waterFlake> 
  6.     </div> 
  7.     <button style="padding : 20px; align-content : center; background-color : #222222;" 
  8.             onclick="reset">重置 
  9.     </button> 
  10.  
  11. </div> 
  1. <!-- index.js --> 
  2. import prompt from '@system.prompt'
  3. export default { 
  4.     data() { 
  5.         return { 
  6.             ballList: [] 
  7.         } 
  8.     }, 
  9.     onInit() { 
  10.         this.ballList = this.genRandomArray(5); 
  11.     }, 
  12.     onBallClick(info) { 
  13.         console.info('xwg parent  onBallClick item = ' + JSON.stringify(info.detail)); 
  14.         let content = info.detail.content 
  15.         prompt.showToast({message:`點擊了${content}`,duration:1500}) 
  16.     }, 
  17.     reset() { 
  18.         console.info("xwg reset clicked "
  19.         this.ballList = this.genRandomArray(6); 
  20.         console.info("xwg reset  ballList = " + JSON.stringify(this.ballList)) 
  21.     }, 
  22.     genRandomArray(count) { 
  23.         let ballArray = [] 
  24.         for (var index = 0; index < countindex++) { 
  25.             let v = this.random(1, 60) 
  26.             ballArray.push(parseInt(v)) 
  27.         } 
  28.         return ballArray 
  29.     }, 
  30.     random(minmax) { 
  31.         return Math.floor(Math.random() * (max - min)) + min
  32.     } 

想了解更多內(nèi)容,請訪問:

51CTO和華為官方合作共建的鴻蒙技術(shù)社區(qū)

https://harmonyos.51cto.com

 

責(zé)任編輯:jianghua 來源: 鴻蒙社區(qū)
相關(guān)推薦

2021-08-09 10:24:49

鴻蒙HarmonyOS應(yīng)用

2021-08-02 14:54:50

鴻蒙HarmonyOS應(yīng)用

2021-03-10 15:03:40

鴻蒙HarmonyOS應(yīng)用

2021-04-29 14:32:24

鴻蒙HarmonyOS應(yīng)用

2021-08-03 10:07:41

鴻蒙HarmonyOS應(yīng)用

2021-04-28 09:56:44

鴻蒙HarmonyOS應(yīng)用

2021-08-26 16:07:46

鴻蒙HarmonyOS應(yīng)用

2021-06-28 14:48:03

鴻蒙HarmonyOS應(yīng)用

2021-01-18 09:52:20

鴻蒙HarmonyOS開發(fā)

2021-02-04 09:45:19

鴻蒙HarmonyOS應(yīng)用開發(fā)

2021-03-24 09:30:49

鴻蒙HarmonyOS應(yīng)用

2021-08-03 12:47:58

鴻蒙HarmonyOS應(yīng)用

2021-04-28 15:07:06

鴻蒙HarmonyOS應(yīng)用

2021-03-01 14:00:11

鴻蒙HarmonyOS應(yīng)用

2021-01-27 10:04:46

鴻蒙HarmonyOS動畫

2021-11-17 15:37:43

鴻蒙HarmonyOS應(yīng)用

2021-08-10 15:23:08

鴻蒙HarmonyOS應(yīng)用

2021-10-19 10:04:51

鴻蒙HarmonyOS應(yīng)用

2021-07-30 14:54:54

鴻蒙HarmonyOS應(yīng)用

2021-04-08 14:57:52

鴻蒙HarmonyOS應(yīng)用
點贊
收藏

51CTO技術(shù)棧公眾號