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

中软小助手
发布于 2022-7-22 09:41
浏览
3收藏

作者:杨尚晓

本文正在参加星光计划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;
}

#夏日挑战赛# OpenHarmony - 基于ArkUI(JS)实现色相滑块组件-鸿蒙开发者社区

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开发技术,欢迎投稿和订阅,让我们一起携手前行共建鸿蒙生态。

©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
7
收藏 3
回复
举报
3条回复
按时间正序
/
按时间倒序
红叶亦知秋
红叶亦知秋

感谢大佬造的轮子,颜色渐变直接修改其他组件的这个思路值得学习。

回复
2022-7-22 10:54:53
只有敬亭山
只有敬亭山

向大佬学习

回复
2022-7-22 10:59:10
Whyalone
Whyalone

记笔记:

// 将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;
    }
},
1
回复
2022-7-22 11:02:09
回复
    相关推荐