天天看點

#夏日挑戰賽# HarmonyOS - 自定義元件之switch開關

作者:姚顯春

本文正在參加星光計劃3.0–夏日挑戰賽

前言

最近在開發FA項目時使用到switch開關元件,該switch元件是基于JS擴充的類Web開發範式元件中的基礎元件 。在使用的過程中發現了一些問題,比如:

1、設定寬高等樣式屬性不會改變switch元件本身的寬高,而是會修改switch元件的内邊距;

2、switch元件存在一定比例的縮放。

使用switch元件在頁面布局時存在上述等問題,但可通過一些技術手段(transform屬性的scale)或一些替代方案,達到預期效果。基于此,通過現在已掌握的FA相關知識實作一個簡單的自定義switch開關元件。

效果展示

#夏日挑戰賽# HarmonyOS - 自定義元件之switch開關

實作原理

基于css關鍵幀動畫animation和@keyframes,通過對switch的滑塊設定css屬性position: absolute,并添加相應的動畫class,即可達到switch開關的過渡效果。給switch設定固定的寬度和高度,保證switch元件不被縮放,在switch元件的外層包裹div元件,來實作布局的合理性。

css動畫樣式官方指導API:

屬性名稱 資料類型 預設值 描述
transform string - 支援同時設定平移/旋轉/縮放等屬性。
animation6+ string 0s ease 0s 1 normal none running none 格式:duration | timing-function | delay | iteration-count | direction | fill-mode| play-state | name,每個字段不區分先後,但是 duration / delay 按照出現的先後順序解析。
animation-name string - 指定@keyframes的名稱。
animation-fill-mode string none 指定動畫開始和結束的狀态:1、none:在動畫執行之前和執行之後都不會應用任何樣式到目标上;2、forwards:在動畫結束後,目标将保留動畫結束時的狀态(在最後一個關鍵幀中定義);3、backwards6+:動畫将在animation-delay期間應用第一個關鍵幀中定義的值。當animation-direction為"normal"或"alternate"時應用from關鍵幀中的值,當animation-direction為"reverse"或"alternate-reverse"時應用to關鍵幀中的值。4、both6+:動畫将遵循forwards和backwards的規則,進而在兩個方向上擴充動畫屬性。
animation-duration <time> 定義一個動畫周期。支援的機關為[s(秒) | ms(毫秒) ],預設機關為ms,格式為:1000ms或1s。(注意:animation-duration 樣式必須設定,否則時長為 0,則不會播放動畫。)
animation-delay <time> 定義動畫播放的延遲時間。支援的機關為[s(秒) | ms(毫秒) ],預設機關為ms,格式為:1000ms或1s。
translateX <length> | <percent> - X軸方向平移動畫屬性。

實作過程

switch元件 hml 部分:

<div class="define-switch">
    <!-- switch 滑塊背景包裹節點 -->
    <div class="switch-wrapper {{ isChecked ? 'bg-blue' : 'bg-grey'}}" onclick="handleClick"></div>
    <!-- switch 滑塊 -->
    <div class="switch-block {{ isChecked ? 'closed' : 'opened'}}"></div>
</div>
           

switch元件 css 部分:

.define-switch {
    position: relative;
    padding: 2px;
}
.switch-wrapper {
    position: relative;
    width: 36px;
    height: 20px;
    background-color: rgba(0,0,0,0.05);
    border-radius: 25px;
}
.switch-block {
    position: absolute;
    left: 2px;
    top: 2px;
    width: 16px;
    height: 16px;
    background-color: #FFFFFF;
    border-radius: 8px;
}
.opened {
    animation-name: open;
    animation-duration: 1s;
    animation-fill-mode: forwards;
}
.closed {
    animation-name: close;
    animation-duration: 1s;
    animation-fill-mode: forwards;
}
.bg-blue {
    background-color: #0A59F7;
}
.bg-grey {
    background-color: rgba(0,0,0,0.05);
}
@keyframes open {
    from {
        transform: translateX(16px);
    }
    to {
        transform: translateX(0px);
    }
}
@keyframes close {
    from {
        transform: translateX(0px);
    }
    to {
        transform: translateX(16px);
    }
}
           

switch元件 js 部分:

export default {
    name: 'DefineSwitch',
    data() {
        return {
            isChecked: false,
        }
    },
    handleClick(evt) {
        console.log('事件對象event:' + JSON.stringify(evt) + this.isChecked);
        this.isChecked = ! this.isChecked; // 修改isChecked的值
        this.$emit('switchChange', {checked: this.isChecked}); // 向父元件傳遞值
    }
}
           

父元件 hml中:

<!-- 引入元件 -->
<element name="mySwitch" src="../mySwitch/mySwitch.hml"></element>
<!-- 元件使用 -->
<div style="width: 40px; height: 24px;">
    <mySwitch @switch-change="handleSwitchChange"></mySwitch>
</div>
           

父元件 js中:

handleSwitchChange(e) {
    console.log('子元件傳來的資料' + e.detail.checked); // 父元件中擷取到switch的開關狀态
}
           

實作中遇到的問題及解決方案:

問題描述:在實作關鍵幀動畫時switch滑塊執行完最後一幀動畫後會回到原來的位置,導緻動畫執行效果不理想。

原因分析:動畫屬性animation的animation-fill-mode屬性預設為none( 在動畫執行之前和之後都不會應用任何樣式到目标上 )。

解決方案:将animation-fill-mode屬性設定為 forwards ( 在動畫結束後,目标将保留動畫結束時的狀态 )。

總結

此自定義元件是對css關鍵幀動畫、父子元件通信的回顧,可能存在一些問題,望大家指正^_^。

更多原創内容請關注:中軟國際 HarmonyOS 技術團隊

入門到精通、技巧到案例,系統化分享HarmonyOS開發技術,歡迎投稿和訂閱,讓我們一起攜手前行共建鴻蒙生态。

繼續閱讀