3

#夏日挑战赛# OpenHarmony - 基于ArkUI(JS)实现色相滑块组件-开源基础软件社区-51CT...

 2 years ago
source link: https://ost.51cto.com/posts/14904
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

#夏日挑战赛# OpenHarmony - 基于ArkUI(JS)实现色相滑块组件 原创 精华

作者:杨尚晓

本文正在参加星光计划3.0–夏日挑战赛

因为项目上需要,并且该组件目前还没见到社区有其他童鞋写过,所以想着自己造一下轮子,帮OpenHarmony建立生态出一份微薄之力。组件其实基本功能已经实现,但是并非最理想的,后续会进行维护更新,继续完善更多功能。

开发环境说明

  • 工具版本:OpenHarmony DevEco Studio 3.0 Beta3
  • SDK版本:3.0.0.901(API Version 8 Beta3)
  • 组要组件:组件名称yg-slider
#夏日挑战赛# OpenHarmony - 基于ArkUI(JS)实现色相滑块组件-开源基础软件社区
属性名 类型 默认值 作用
g-color Array [] 渐变色值组,eg: #ff9800
step Number 20 步长,g-color属性里每两渐变色之间步长,步长越大 越精准,渲染压力越大
show-card Boolean false 是否显示滑块上方的颜色卡片
属性名 类型 返回值 备注
get-result Function {num:String, rgbColor:String, hexColor:String, hex:Object} num: 当前滑块下标值, rgbColor: rgb颜色值【rgb(0,0,0)】, hex颜色值【##ff9800】,hex: R,G,B对应16进制hex值

在需要引用的hml中element引入组件

<element name="yg-slider" src="../../common/component/ygSlider/ygSlider"></element>
<div class="container">
    <yg-slider
        g-color="{{gColor}}"
        step="{{step}}"
        show-card="{{showCard}}"
        @get-result="getResult"
    ></yg-slider>
</div>

js中的传参和绑定的事件

import Log  from "../../common/utils/log"
const lg = new Log('index页面'); // 可以传入一个值作为该页面的标识
export default {
    data: {
        gColor: [
            '#FF0000',
            '#FFE300',
            '#74FF00',
            '#00FFA6',
            '#00FDFF',
            '#0034FC',
            '#4200FF',
            '#BC00FF',
            '#FD00FF',
            '#FF0000'
        ],
        step: 20,
        showCard: true,
    },
    onInit() {

    },
    getResult(res){
        lg.info(res._detail)
    },
}
  1. 根据UI写好样式。
  2. 绑定滑块的触摸事件
  3. 获取滑块条的长度和位置,和当前触摸的位置做比较,通过计算获取滑块所在的位置
  4. 封装获取两颜色之间的渐变值组
  5. 根据渐变值组总长度,设置为滑块的刻度值。
  6. 滑块滑动到某位置计算该位置刻度,得到渐变值组的对应色值,赋值给颜色卡片展示

1. 根据UI写好页面和样式

ygSlider.hml页面

<div class="container">
    <div class="yg-slider" ref="ygSlider" id="ygSlider">
        <div class="yg-slider-bg"></div>
        <div class="yg-slider-button">
            <div class="yg-slider-button-child"></div>
        </div>
        <div class="yg-slider-color"></div>
    </div>
</div>

ygSlider.css

.yg-slider{
    width: 320px;
}
.yg-slider-bg{
    background: linear-gradient(90deg, #FF0000, #FFE300, #74FF00, #00FFA6, #00FDFF, #0034FC, #4200FF, #BC00FF, #FD00FF,#FF0000);
    width: 100%;
    height: 4px;
    border-radius: 16px;
}
.yg-slider-button{
    /* 设置热区 */
    position: absolute;
    top: -16px;
    transform: translateX(-18px);
    left: 0%;
    width: 36px;
    height: 36px;
    background-color: rgba(52,0,250,.5);
    border-radius: 16px;
    display: flex;
    justify-content: center;
    align-items: center;
}
.yg-slider-button-child{
    border-radius: 8px;
    width: 16px;
    height: 16px;
    background-color: rgba(255,255,255,1);
    box-shadow: 0 0 1px 1px rgba(0,0,0,.2);
}
.yg-slider-color{
    position: absolute;
    top: -80px;
    transform: translateX(-20px);
    width: 40px;
    height: 40px;
    border-radius: 40px;
    background-color: red;
}

2. 绑定滑块的触摸事件

ygSlider.hml

<div class="yg-slider-button"
     style="left: {{pct}}%;"
     @touchstart="sliderStart"
     @touchmove="sliderMove"
     @touchend="sliderEnd"
>

ygSlider.js

在sliderStart事件里,通过getBoundingClientRect()方法获取滑块条的宽高和位置

sliderStart(){
    const t = this.$refs.ygSlider.getBoundingClientRect();
    this.startX = t.left;
    this.endX = t.left + t.width;
    this.sliderW = t.width;
 },

在sliderMove事件里设置pct为滑块按钮的位置,并确保按钮不会超出滑块条

sliderMove(e){
    let x = e.touches[0].globalX;
    let s_x = x - this.startX;
    if(x <= this.startX){
        this.pct = 0;
    } else if(x >= this.endX){
        this.pct = 100;
    } else {
        this.pct =  s_x * 100 / this.sliderW;
    }
},

3. 封装获取两颜色之间的渐变值组的方法

封装颜色渐变之间值方法,有备注。这个方法在之前的一个组件中就开始实现了,搞好需要就拿过来修改了一下。
传送门: HarmonyOS 基于JSAPI自定义封装渐变圆环进度条组件

/**
 * @description: 封装颜色渐变之间值
 * @param {String} startColor 开始颜色hex
 * @param {Number} endColor 结束颜色hex
 * @param {Number} step 渐变精度
 * @return {Array}
 */
gradientColor(startColor,endColor,step){
    let startRGB = this.hexToRgb(startColor);//转换为rgb数组模式
    let endRGB = this.hexToRgb(endColor);

    let sR = (endRGB[0]-startRGB[0])/step;//总差值
    let sG = (endRGB[1]-startRGB[1])/step;
    let sB = (endRGB[2]-startRGB[2])/step;
    var colorArr = [];
    for(var i=0;i<step;i++){
        //计算每一步的hex值
        var hex = `rgb(${parseInt((sR*i+startRGB[0]))},${parseInt((sG*i+startRGB[1]))},${parseInt((sB*i+startRGB[2]))})`;
        colorArr.push(hex);
    }
    return colorArr;
},


// 将hex表示方式转换为rgb表示方式(这里返回rgb数组模式)
hexToRgb(sColor){
    var reg = /^#([0-9a-fA-f]{3}|[0-9a-fA-f]{6})$/;
    sColor = sColor.toLowerCase();
    if(sColor && reg.test(sColor)){
        if(sColor.length === 4){
            var sColorNew = "#";
            for(var i=1; i<4; i+=1){
                sColorNew += sColor.slice(i,i+1).concat(sColor.slice(i,i+1));
            }
            sColor = sColorNew;
        }
        //处理六位的颜色值
        var sColorChange = [];
        for(let i=1; i<7; i+=2){
            sColorChange.push(parseInt("0x"+sColor.slice(i,i+2)));
        }
        return sColorChange;
    }else{
        return sColor;
    }
},

// 将rgb表示方式转换为hex表示方式
rgbToHex(rgb){
    var reg = /^#([0-9a-fA-f]{3}|[0-9a-fA-f]{6})$/;
    if(/^(rgb|RGB)/.test(rgb)){
        let aColor = rgb.replace(/(?:\(|\)|rgb|RGB)*/g,"").split(",");
        let strHex = "#";
        for(let i=0; i<aColor.length; i++){
            let hex = this.getHexNumber(aColor[i]);
            if(hex === "0"){
                hex += hex;
            }
            strHex += hex;
        }
        if(strHex.length !== 7){
            strHex = rgb;
        }
        return strHex;
    }else if(reg.test(rgb)){
        var aNum = rgb.replace(/#/,'').split('');
        if(aNum.length === 6){
            return rgb;
        }else if(aNum.length === 3){
            var numHex = "#";
            for(let i=0; i<aNum.length; i+=1){
                numHex += (aNum[i]+aNum[i]);
            }
            return numHex;
        }
    }else{
        return rgb;
    }
},

//将hexColor或者rgbColor转换hex的RGB值对象
colorToHex(rgb){
    if(rgb && rgb.indexOf('#') > -1){
        var aNum = rgb.replace(/#/,'').toUpperCase().split('');
        if(aNum.length === 6){
            return {
                R: aNum[0] + aNum[1],
                G: aNum[2] + aNum[3],
                B: aNum[4] + aNum[5]
            };
        }else if(aNum.length === 3){
            return {
                R: aNum[0] + aNum[0],
                G: aNum[1] + aNum[1],
                B: aNum[2] + aNum[2]
            };
        } else {
            return rgb;
        }
    }  else if(/^(rgb|RGB)/.test(rgb)){
        let aColor = rgb.replace(/(?:\(|\)|rgb|RGB)*/g,"").split(",");
        let R = this.getHexNumber(aColor[0]);
        let G = this.getHexNumber(aColor[1]);
        let B = this.getHexNumber(aColor[2]);
        return {R,G,B};
    } else {
        return rgb;
    }
},

getHexNumber(str){
    if(typeof str === 'string'){
        return Number(str).toString(16).padStart(2,'0').toUpperCase()
    } else {
        return str;
    }
}

因为传进来的颜色组不止两个,所以做了一个循环处理

onPageShow(){
    this.setColorList();
},
//将传进来的颜色生成一个渐变色值组
setColorList(){
    for(let i = 0; i < this.gColor.length - 1; i++){
        let res = this.gradientColor(this.gColor[i], this.gColor[i+1], this.step)
        this.colorList.push(...res)
    }
    // 设置滑块可以展示的最大值
    this.maxValue = this.colorList.length - 1;
    
    // 展示卡片默认显示数组里的第一个色值
    this.nowColor = this.colorList[0]
},

4. 根据渐变值组总长度,设置为滑块的刻度值

sliderMove(e){
    let x = e.touches[0].globalX;
    let s_x = x - this.startX;
    if(x <= this.startX){
        this.pct = 0;
    } else if(x >= this.endX){
        this.pct = 100;
    } else {
        this.pct =  s_x * 100 / this.sliderW;
    }

    // 计算当前的刻度值
    let r = this.getValue(s_x);
    // 设置滑块按钮滑到所在的位置的的色值
    this.nowColor = this.colorList[r];

    //最后将每次滑块所变更的值都抛出给父组件去调用做相应的处理
    this.$emit('getResult', {
        num: r,
        rgbColor: this.nowColor,
        hexColor: this.rgbToHex(this.nowColor),
        hex: this.colorToHex(this.nowColor),
    })
},
sliderEnd(e){
    let x = e.touches[0].globalX;
    let s_x = x - this.startX;
    let r = this.getValue(s_x);
    this.nowColor = this.colorList[r];
    this.$emit('getResult', {
        num: r,
        rgbColor: this.nowColor,
        hexColor: this.rgbToHex(this.nowColor),
        hex: this.colorToHex(this.nowColor),
    })
},
// 获取当前刻度值
getValue(s_x){
    let r = Math.ceil(this.maxValue * s_x / this.sliderW);
    if(r <= 0){
        r = 0;
    } else if(r >= this.maxValue) {
        r = this.maxValue;
    }
    return r;
},

#夏日挑战赛# openHarmony - 基于ArkUI(JS)实现色相滑块组件

难点:逻辑都比较简单,主要难点在于就是计算两颜色值之间的渐变色值组。
和计算当前刻度,这里需要注意三个值

  • 滑块按钮的当前位置 0% -100%
  • 滑块条的长度,比如是320px
  • 滑块的刻度

三者之间需要进行计算转换。

更多原创内容请关注:中软国际 HarmonyOS 技术团队

入门到精通、技巧到案例,系统化分享HarmonyOS开发技术,欢迎投稿和订阅,让我们一起携手前行共建鸿蒙生态。

©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK