记录一次关于小程序rpx的坑

239 阅读3分钟

记录一次关于小程序rpx的坑

相关知识点:小程序,background-position,rpx,百分比,taro + react + hooks

背景:

    公司的抽奖活动改成了老虎机的那种形式。类似下面这样。

于是参考社区的这篇文章

当时写的时候就是想封装成一个公共的组件,因为这是公司第一次的老虎机抽奖,后续有类似活动,可以直接使用我的组件,代码后面贴出来。

大致思路

所以大致思路:利用background-position 能够改变背景图片的相对位置 + transition 实现动态效果。

为什么不使用, transfrom: translateY ? 因为需要动态去计算元素的搞,增加额外的dom 消耗。

简单说下,计算滚动的距离:

**计算规则:**每一个icon 的高度 x 圈数 x 最后定位到第几个的高度。其中圈数的高度: 圈数 x icon 的个数

 (position - 1 + turnC * count) * height

具体问题

在使用background-position 的时候,第一默认想法是 使用rpx 去计算 需要滚动的高度。

做到这里一切都在预料之中,没什么问题,但是当提测以后,ui发现iphone 12上面怎么都无法剧中对齐,总是偏差一点距离,这是为什么呢?

苦思冥想了好久,测试了好久发现:当我把圈数调的越大,这个差距就会越明显?但是除了12以外其机型都是完美剧中,并且得出结论:计算公式没有错误,但是iphone12 上面会有计算偏差,而且偏差会根据偏移值越大而越大。

于是使用了百分比去进行位移操作以后就不会出现偏差了。代码如下:

公共组件:

/**
 * @Owners ph
 * @Title 老虎机-滚动效果
 */
import { Image, View } from '@tarojs/components';
import Taro from '@tarojs/taro';
import { cIcon } from 'mj-mall-taro-consts';
import { Ref, forwardRef, useImperativeHandle, useState } from 'react';

import './wheel.scss';

type Config = {
    // 每个icon的宽高
    width?: number,
    height?: number,
    // 初始位置是第几个 -- 默认第一个
    initPos?: number,
    // 总个数
    count?: number,
    // 转几圈 -- 默认一圈
    turnCount?: number,
    bg?: string,
};
export type Transition = {
    // 转的速度: 完成的时间
    duration?: number,
    // 转的方式
    timingFunction?: string | 'ease-in-out' | 'ease-in' | 'ease-out' | 'ease' | 'linear',
    // 延迟的时间
    delay?: number,
};

const ratio: number = Taro.getStorageSync('ratio') || 2;

type Props = {
    config?: Config,
    initTransition?: Transition,
};
const Wheel = (props: Props, ref: Ref<unknown> | undefined) => {
    const { config, initTransition } = props;
    const {
        count = 4,
        width = 142,
        height = 172,
        initPos = 1,
        turnCount = 1,
        bg = cIcon.LUCKY_SLOT_MACHINE_WHEELS,
    } = config || {};
    const {
        duration = 2.5,
        timingFunction = 'ease-in-out',
        delay = 0,
    } = initTransition || {};
    // 转动到第几个 转动的时间 延时
    const [transition, updateTransition] = useState<Transition>({
        duration,
        timingFunction,
        delay,
    });
    // 转以后定位到第几个 -- 默认第一个
    const [position, updatePosition] = useState<number>(initPos);
    // 转几圈
    const [turnC, updateTurnC] = useState<number>(turnCount);
    // 是否开始了
    const [isStart, updateIsStart] = useState<boolean>(false);

    const doAnimation = (p: number, _turnCount?: Transition | number, pos?: Transition) => {
        updateIsStart(true);
        updatePosition(p);
        if (typeof _turnCount === 'object') {
            updateTransition({
                ...transition,
                ..._turnCount,
            });
        } else {
            _turnCount && updateTurnC(_turnCount);
            updateTransition({
                ...transition,
                ...pos,
            });
        }
        // 重制
        // rest()
    };

    useImperativeHandle(ref, () => ({
        doAnimation,
        // 重制,待实现
        // reset: () => {},
    }));
    return <View className='wheel-container' style={{
        width: (width / ratio) + 'px',
    }}>
        <Image className='wheel-shadow' src={cIcon.LUCKY_SLOT_MACHINE_SHADOW} />
        <View className='wheel-box-image' style={{
            backgroundImage: `url(${bg})`,
            width: (width / ratio) + 'px',
            height: (height / ratio) + 'px',
            // backgroundPosition: `0px ${isStart ? `-${(position - 1 + turnC * count) * height / ratio}px` : `-${(position - 1) * height / ratio}px`}`,
            backgroundPosition: `0px ${isStart ? `-${(position - 1 + turnC * count) * 100}%` : `-${(position - 1) * height / ratio}px`}`,
            transition: `all ${transition.duration}s ${transition.timingFunction} ${transition.delay}s`,
        }} />
    </View>;
};

export default forwardRef(Wheel);

使用:

        // ref
        const getWheel_one = useRef<{
            doAnimation(posi: number, turnCount?: number, transi?: Transition): void,
        }>();
        
        // 配置初始化
        <Wheel
          ref={getWheel_one}
          config={{
            initPos: 1,
       }} />
        
       // 调用
        const position = 2;
        const DURATION1 = 2;
           // 转多少圈
        const turnCount1 = 2;
        getWheel_one.current?.doAnimation(position, turnCount1, {
           duration: DURATION1,
           timingFunction: 'cubic-bezier(0.52, 0.06, 0.3, 1)',
        });

所以大家知道为什么 iphone 12 会计算出现偏差吗?