背景

h5页面有时候需要给可交互元素添加触摸反馈功能:

  • 点击的时候给触摸元素1个样式的修改
  • 取消点击的时候还原

image

本文首先对多种已知方法进行描述,同时总结其优缺点,最后基于以上方法提出我们自己的方法

法1::active

使用css的active伪选择器,代码如下

button {
    transition: background-color .2s;
    &:active {
        background-color: #f0f;
    }
}

优点

  • 简单直接,代码量小且纯css原生实现

缺点

  • 代码重复,页面的可交互元素一般是非常多的,每个元素都实现一遍上述代码会造成重复
缺点: 无法复用

法2:-webkit-tap-highlight-color

缺点

  • 非标准属性
  • 平台有限制:只适用于ios
  • 适用元素有限制:a标签

基本不可用

法3:vue指令 + touch事件监听 实现

借助vue的directive来实现自定义指令

    const touchFeedback: DirectiveOptions = {
        bind(el) {
            el.addEventListener('touchstart', touchStartHandle, {
                passive: true,
            });
            el.addEventListener('touchmove', touchMoveHandle, {
                passive: true,
            });
            el.addEventListener('touchend', touchCancelHandle);
            el.addEventListener('touchcancel', touchCancelHandle);
        },
    };

具体实现基本是在el点击时设置其特殊样式(background-color),取消点击时还原;
使用时大概是这样

<div v-touch-feedback="#ababab">点击我</div>

这个方案是现阶段比较常见的实现方式

优点

  • 兼容性强
  • 代码一定程度复用

缺点

  • 需要传递当前组件的active背景色作为参数
  • 每一个dom都要绑定多个touch事件,比较繁重

移动端页面内经常会有大量的可点击元素,每一个都需要我们具体设定下背景色还是有重复的工作量在里面的。

基于已上方法的缺陷,提出一个更加简洁并且使用更方便的触摸反馈实现方案。

最终方案:css变量 + HSL

这种实现方案需要借助几项原生功能

这几种技术已经被绝大多数浏览器支持,基本可以放心使用;

自动计算active颜色

针对上一方案问题1,我们常见操作是从取色板取一个与当前颜色一致但是亮度稍暗的一个颜色作为背景色;

image

那么一个自然的想法是否可以自动计算一个颜色的更暗的色值呢?答案是可以的,这就是HSL;

不同于rgb这种混合颜色模型,hsl是(色相, 饱和度, 亮度)模式,可以通过修改亮度维度值来获得我们需要的颜色

image

计算active颜色代码如下

const activeColor = async function(el: HTMLElement) {
    // 获取当前背景色
    const rgba = window.getComputedStyle(el).backgroundColor;
    const [r, g, b, a = 1] = /^rgba?\(([^()]+)\)$/.exec(rgba)?.[1];

    // rgb => hsl
    const [h, s, l] = rgb2hsl(r, g, b);
    const sPer = (s * 100).toFixed(1);

    // 计算低亮度颜色
    const lPer = (l * 0.5 * 100).toFixed(1);

    return `hsla(${h}, ${sPer}%, ${lPer}%, ${a})`;
};

其中

  • rgb2hsl是将rgb转换为hsl,代码网上有很多不再赘述
  • 最后返回的颜色,将亮度l除以2,即取一半的亮度

响应用户点击行为

这里依然使用:active伪选择器方式来实现,但是需要使用js来调整:active中的颜色。

但是:active无法设置在style属性中,因此传统的js控制style的方式无法实现,我们需要使用css变量来达到目的;

  1. 首先设置css

    .g-touch-feedback {
     // disabled属性,disabled类 不响应反馈
     &:not(.disabled):not([disabled]) {
         &:active {
             background-color: var(--feedback-active) !important;
         }
     }
    }

    以上css包含了几个特性

  2. 所有带有g-touch-feedback类名的元素,为可反馈的
  3. 当元素带有disabled属性,或者有disabled类名时;我们认为元素禁用,此时不可反馈
  4. active时的背景颜色,使用--feedback-active变量
  5. 然后js需要和css配合起来,下面是指令代码
const touchFeedback: DirectiveOptions = {
    bind(el, binding) {
        el.classList.add('g-touch-feedback');
        el.style.transition = 'background-color .2s';

        const activeColor = calcBackColor(el);
        el.style.setProperty('--feedback-active', activeColor);
    },
    componentUpdated(el) {
        // class 更新后可能会被移除
        el.classList.add('g-touch-feedback');
    },
};
  1. 使用
<div v-touch-feedback>点击我</div>

PS补充

实际代码中亮度计算公式为

L = 0.95 - 0.2 * ((H * S) / 360)

这里是为了调整L亮度的值范围为原来的95%-75%;
为什么不是所有颜色使用统一的比例进行加深呢?因为人眼对不同频率光敏感度不同。给人的感觉是浅颜色变化快,深颜色变化慢。
为了能够达到均匀的颜色变化,需要综合颜色的H(色相)和S(饱和度) 来考虑颜色加深比例。
最终唯一修改的只有L,H和S只是用来作为计算因子


defghy
169 声望8 粉丝