Taro React组件使用(11) —— RuiNoticebar 公告栏

news2024/11/24 16:12:45

1. 需求分析

  1. 用于循环播放展示一组消息通知;
  2. 通知消息渲染完成,获取消息的长度和盒子的长度;
  3. 使用【taro react】---- 获取元素的位置和宽高等信息异步获取内容和盒子的宽高信息;
  4. 通过 CSS3 的 animation 实现内容的移动;
  5. 注意:第一次移动和第二次移动的长度不相同,因此需要监听第一次动画完成 onAnimationEnd。

2. 异步获取元素的宽高信息

import { createSelectorQuery } from '@tarojs/taro';
function isWindow(val){
  return val === window
}

export const getRect = (elementRef) => {
  const element = elementRef
  // 判断传入元素是否是window窗口,是window窗口,直接获取窗口的宽高
  if (isWindow(element)) {
    const width = element.innerWidth
    const height = element.innerHeight

    return {
      top: 0,
      left: 0,
      right: width,
      bottom: height,
      width,
      height,
    }
  }
  // 是元素,同时可以获取元素的宽高等信息
  if (element && element.getBoundingClientRect) {
    return element.getBoundingClientRect()
  }
  // 都不满足,返回默认值
  return {
    top: 0,
    left: 0,
    right: 0,
    bottom: 0,
    width: 0,
    height: 0,
  }
}

export const getRectByTaro = async (element) => {
  // 元素存在,判断使用对应环境获取元素信息
  if (element) {
    if(process.env.TARO_ENV === "h5"){
      // H5环境使用元素的获取元素信息方法
      return Promise.resolve(getRect(element))
    } else if(process.env.TARO_ENV === "weapp"){
      // 微信小程序环境调用 boundingClientRect 获取元素信息
      return new Promise((resolve) => {
        createSelectorQuery()
          .select(`.${element.props.class.split(' ').filter(item => item).join('.')}`)
          .boundingClientRect(resolve).exec()
      })
    }
  }
  // 返回默认值
  return Promise.resolve({
    top: 0,
    left: 0,
    right: 0,
    bottom: 0,
    width: 0,
    height: 0,
  })
}

3. 监听内容加载获取盒子和内容元素信息

  1. 初始化加载如果又内容,就初始化滚动参数;
  2. 使用 setTimeout,实现先渲染,后获取渲染后的元素信息;
  3. 判断盒子和内容是否都渲染完成,没有渲染就直接返回;
  4. 通过 getRectByTaro 获取盒子和内容的宽度;
  5. 判断 scrollable 是否允许进行消息滚动;
  6. 判断内容和盒子的宽度,如果内容小于盒子,则不进行消息滚动;
  7. 如果可以消息滚动,则记录盒子和内容的宽度,计算动画滚动的时间;
  8. 设置第一次滚动的className;
  9. 不能滚动,就清除滚动动画的className。
  // 初始化加载如果又内容,就初始化滚动参数
  useEffect(() => {
    initScrollWrap(content)
  },[content])
  // 初始化设置函数
  const initScrollWrap = (value) => {
    let timer = setTimeout(async () => {
      clearTimeout(timer)
      if(!wrapRef.current || !contentRef.current){
        return
      }
      const wrapObj = await getRectByTaro(wrapRef.current)
      const wrapW = wrapObj.width
      const offsetObj = await getRectByTaro(contentRef.current)
      const offsetW = offsetObj.width
      const canScroll = scrollable == null ? offsetW > wrapW : scrollable
      if (canScroll) {
        setWrapWidth(wrapW)
        setOffsetWidth(offsetW)
        setAnimationDuration(offsetW / speed)
        setAnimationClass('rui-play')
      } else {
        setAnimationClass('')
      }
    },0)
  }

4. 监听第一次结束设置后续动画

  1. 第一次动画结束,将判断第一次动画的变量设置为 false;
  2. 异步设置计算动画的时间;
  3. 设置无限循环移动的动画className。
  // 第一次运动结束后设置新的时间和动画
  const onAnimationEnd = () => {
    setFirstRound(false)
    let timer = setTimeout(() => {
      setAnimationDuration((offsetWidth + wrapWidth) / speed)
      setAnimationClass('rui-play-infinite')
      clearTimeout(timer)
    }, 0)
  }

5. CSS3 移动动画

.rui-play-infinite{
    animation: rui-notice-bar-play-infinite linear infinite both running;
  }
  .rui-play{
    animation: rui-notice-bar-play linear both running;
  }
  @keyframes rui-notice-bar-play {
    to {
      transform: translate3d(-100%,0,0)
    }
  }

  @keyframes rui-notice-bar-play-infinite {
    to {
      transform: translate3d(-100%,0,0)
    }
  }

6. 完整 JSX 代码

import { View } from '@tarojs/components';
import { useAsyncState } from '@utils/event';
import { useEffect, useRef } from 'react';
import { getRectByTaro } from '@utils/use-client-rect';
import './index.scss'

const RuiNoticebar = (props) => {
  let {
    children,
    content,
    scrollable = null,
    speed = 50,
    delay = 1
  } = props;
  // 获取盒子和内容的宽度,设置移动的时间,是否是第一次移动,以及移动的class
  let wrapRef = useRef(null)
  let contentRef = useRef(null)
  let [wrapWidth, setWrapWidth] = useAsyncState(0)
  let [offsetWidth, setOffsetWidth] = useAsyncState(0)
  let [animationDuration, setAnimationDuration] = useAsyncState(0)
  let [firstRound, setFirstRound] = useAsyncState(true)
  let [animationClass, setAnimationClass] = useAsyncState('')
  // 初始化加载如果又内容,就初始化滚动参数
  useEffect(() => {
    initScrollWrap(content)
  },[content])
  // 初始化设置函数
  const initScrollWrap = (value) => {
    let timer = setTimeout(async () => {
      clearTimeout(timer)
      if(!wrapRef.current || !contentRef.current){
        return
      }
      const wrapObj = await getRectByTaro(wrapRef.current)
      const wrapW = wrapObj.width
      const offsetObj = await getRectByTaro(contentRef.current)
      const offsetW = offsetObj.width
      const canScroll = scrollable == null ? offsetW > wrapW : scrollable
      if (canScroll) {
        setWrapWidth(wrapW)
        setOffsetWidth(offsetW)
        setAnimationDuration(offsetW / speed)
        setAnimationClass('rui-play')
      } else {
        setAnimationClass('')
      }
    },0)
  }
  // 第一次运动结束后设置新的时间和动画
  const onAnimationEnd = () => {
    setFirstRound(false)
    let timer = setTimeout(() => {
      setAnimationDuration((offsetWidth + wrapWidth) / speed)
      setAnimationClass('rui-play-infinite')
      clearTimeout(timer)
    }, 0)
  }
  // 设置内容的动画参数
  const contentStyle = {
    animationDelay: `${firstRound ? delay : 0}s`,
    animationDuration: `${animationDuration}s`,
    transform: `translateX(${firstRound ? 0 : `${wrapWidth}px`})`,
  }
  return <View className='rui-noticebar-temp-content rui-flex-ac'>
    <View 
    className='rui-flex-ac rui-noticebar-wrap-content' 
    ref={wrapRef}>
      <View 
      className={'rui-fg rui-noticebar-content ' + animationClass} 
      style={contentStyle}
      onAnimationEnd={onAnimationEnd} 
      ref={contentRef}>
        { children }
        { content }
      </View>
    </View>
  </View>
}
export default RuiNoticebar;

7. 完整 SCSS 代码

.rui-noticebar-temp-content{
  height: 80px;
  .rui-noticebar-wrap-content{
    flex: 1;
    height: 80px;
    line-height: 80px;
    overflow: hidden;
    position: relative;
  }
  .rui-noticebar-content{
    position: absolute;
    white-space: nowrap;
  }
  .rui-ellipsis {
    max-width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap
  }
  .rui-play-infinite{
    animation: rui-notice-bar-play-infinite linear infinite both running;
  }
  .rui-play{
    animation: rui-notice-bar-play linear both running;
  }
  @keyframes rui-notice-bar-play {
    to {
      transform: translate3d(-100%,0,0)
    }
  }

  @keyframes rui-notice-bar-play-infinite {
    to {
      transform: translate3d(-100%,0,0)
    }
  }
}

8. 效果

输入图片说明

9. 使用实例

import { View, Text, Image } from '@tarojs/components';
import { RuiCustomWhite } from '@com/RuiCustom';
import RuiNoticebar from '@com/RuiNoticebar';
import Img from '@utils/icon/icon';

const NoticebarPage = (props) => {
  return <View className='rui-luck-page-content'>
    <RuiCustomWhite isBorder title='NoticeBar'/>
    <View className='rui-tac rui-mt20 rui-mb20 rui-fs30'>基础使用</View>
    <View className='rui-ml30 rui-mr30 rui-mt15 rui-mb15'>
      <RuiNoticebar content="NutUI 是京东风格的移动端组件库,使用 Vue 语言来编写可以在 H5,小程序平台上的应用,帮助研发人员提升开发效率,改善开发体验。"/>
    </View>
    <View className='rui-tac rui-mt20 rui-mb20 rui-fs30'>添加图标</View>
    <View className='rui-ml30 rui-mr30 rui-mt15 rui-mb15 rui-flex-ac'>
      <Image src={Img.iconAlarmIcon} className='rui-fa rui-icon-36'></Image>
      <View className='rui-fg rui-ml15'>
        <RuiNoticebar content={
          <View className='rui-fs26 rui-colorl6'>NutUI 是京东风格的移动端组件库,使用 Vue 语言来编写可以在 H5,小程序平台上的应用,帮助研发人员提升开发效率,改善开发体验。</View>
        }/>
      </View>
    </View>
    <View className='rui-tac rui-mt20 rui-mb20 rui-fs30'>自定义速度</View>
    <View className='rui-ml30 rui-mr30 rui-mt15 rui-mb15 rui-flex-ac'>
      <Image src={Img.iconAlarmIcon} className='rui-fa rui-icon-36'></Image>
      <View className='rui-fg rui-ml15'>
        <RuiNoticebar speed={100} content={
          <View className='rui-fs26 rui-colorl6'>NutUI 是京东风格的移动端组件库,使用 Vue 语言来编写可以在 H5,小程序平台上的应用,帮助研发人员提升开发效率,改善开发体验。</View>
        }/>
      </View>
    </View>
    <View className='rui-tac rui-mt20 rui-mb20 rui-fs30'>停止滚动</View>
    <View className='rui-ml30 rui-mr30 rui-mt15 rui-mb15 rui-flex-ac'>
      <Image src={Img.iconAlarmIcon} className='rui-fa rui-icon-36'></Image>
      <View className='rui-fg rui-ml15'>
        <RuiNoticebar 
        scrollable={false}
        content={
          <View className='rui-fs26 rui-colorl6'>NutUI 是京东风格的移动端组件库,使用 Vue 语言来编写可以在 H5,小程序平台上的应用,帮助研发人员提升开发效率,改善开发体验。</View>
        }/>
      </View>
    </View>
    <View className='rui-tac rui-mt20 rui-mb20 rui-fs30'>children</View>
    <View className='rui-ml30 rui-mr30 rui-mt15 rui-mb15 rui-flex-ac'>
      <View className='rui-fg'>
        <RuiNoticebar scrollable={false}>
          <View className='rui-fs26 rui-colorl6 rui-flex-ac'>
            <Image src={Img.iconAlarmIcon} className='rui-fa rui-icon-36'></Image>
            <Text className='rui-ml15'>NutUI 是京东风格的移动端组件库。</Text>
          </View>
        </RuiNoticebar>
      </View>
    </View>
  </View>
}
export default NoticebarPage;

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/857152.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Flume拦截器

实现 Interceptor接口 方法1 是初始化: 方法2和3重载 拦截: 方法3 是关闭: 但是flume是通过内部类创建对象的

一、Kubernetes介绍与集群架构

Kubernetes介绍与集群架构 一、认识容器编排工具 docker machine 主要用于准备docker host现已弃用建议使用docker desktop docker compose Compose 是一个用于定义和运行多容器 Docker 应用程序的工具。使用 Compose&#xff0c;您可以使用 YAML 文件来配置应用程序的服务。…

c语言--浮点数(float)与0值比较

我们定义一个float类型的数据a&#xff0c;赋值为123456789并输出&#xff1a; int main() {float a;a 123456789;printf("%f\n",a);return 0; }输出结果为&#xff1a; 由此可以看出&#xff0c;程序输出的结果并不是其被赋予的值。即说明了浮点数存在一定的误差。…

MD-MTSP:星雀优化算法NOA求解多仓库多旅行商问题MATLAB(可更改数据集,旅行商的数量和起点)

一、星雀优化算法NOA 星雀优化算法(Nutcracker optimizer algorithm,NOA)由Mohamed Abdel-Basset等人于2023年提出&#xff0c;该算法模拟星雀的两种行为&#xff0c;即&#xff1a;在夏秋季节收集并储存食物&#xff0c;在春冬季节搜索食物的存储位置。星雀优化算法(Nutcrack…

Optional 使用方法详解

Optional的作用是什么&#xff1f;他都有哪些方法&#xff1f;阿里规范点名说尽量用Optional来避免空指针&#xff0c;那么什么场景用Optional&#xff1f;本篇文章围绕这三点来进行讲解。 一、Optional类的来源 到目前为止&#xff0c;臭名昭著的空指针异常是导致Java应用程…

Go For Web:踏入Web大门的第一步——Web 的工作方式

Web 的工作方式 了解当你访问一个网页时&#xff0c;背后是如何运作的&#xff0c;发生了一些什么&#xff1f; 其实当你输入网址&#xff08;URL&#xff09;并按下回车之后&#xff0c;你的浏览器相当于扮演了 客户端 的角色&#xff0c;首先你的浏览器会去请求 DNS 服务器进…

uniapp软键盘谈起遮住输入框和头部被顶起的问题解决

推荐&#xff1a; pages.json中配置如下可解决头部被顶起和表单被遮住的问题。 { "path": "pages/debug/protocol/tagWord", "style": { "app-plus": { "soft…

外呼系统是什么?有什么功能?AI智能系统人工系统

很多企业往往会选择通过使用外呼系统来解决封卡问题。与传统人工拨打相比&#xff0c;外呼系统一方面可以协助销售进行高效外呼&#xff0c;同时还能保障线路稳定不封号&#xff0c;与系统的接入也可以保证客户信息的跟进&#xff0c;助力销售持续性跟进。但是看到这里也不要误…

php通过各种函数判断0和空php实例

php通过各种函数判断0和空php实例 本文给大家介绍php同各种函数判断0和空的方法&#xff0c;在文章给大家补充介绍了php 语法里0不等于null为空的解决办法 补充&#xff1a;下面给大家介绍下php 语法里0不等于null为空的解决办法 今天遇到这样一个问题是这样的: php 语句里,我…

Allure2测试报告详解

目录&#xff1a; allure2安装allure2运行方式allure2报告生成allure2报告中添加用例标题allure2报告中添加用例步骤allure2报告中添加用例链接allure2报告中添加用例分类allure2报告中添加用例描述allure2报告中添加用例优先级allure2报告中添加用例支持tags标签allure2报告中…

冠达管理:A股继续缩量整理 创业板指数走势相对偏强

周三&#xff0c;A股低开后震动调整&#xff0c;三大指数均小幅跌落&#xff0c;创业板指相对偏强。到收盘&#xff0c;上证综指跌0.49%&#xff0c;报3244.49点&#xff1b;深证成指跌0.53%&#xff0c;报11039.45点&#xff1b;创业板指跌0.01%&#xff0c;报2228.73点。 资金…

用于3D MRI和CT扫描的深度学习模型总结

医学成像数据与其他我们日常图像的最大区别之一是它们很多都是3D的&#xff0c;比如在处理DICOM系列数据时尤其如此。DICOM图像由很多的2D切片组成了一个扫描或身体的特定部分。 那么如何为这类数据构建深度学习解决方案呢?本文中将介绍6种神经网络架构&#xff0c;可以使用它…

Window版 Redis6.0.6安装

一、准备工作 下载Redis6.0.6版本&#xff08;redis官网地址&#xff1a;http://www.redis.cn/download.html&#xff09; 下载MSYS2&#xff08;清华的源&#xff1a;https://mirrors.tuna.tsinghua.edu.cn/msys2/distrib/x86_64/&#xff09; 安装板exe结尾的文件&#xf…

chapter14:springboot与安全

Spring Boot与安全视频 Spring Security, shiro等安全框架。主要功能是”认证“和”授权“&#xff0c;或者说是访问控制。 认证&#xff08;Authentication&#xff09;是建立在一个声明主体的过程&#xff08;一个主体一般指用户&#xff0c;设备或一些可以在你的应用程序中…

【ARM64 ATF 系列 3 -- ARM64 warm reset 与 cold reset】

文章目录 ARM64 Reset1.1.1 reset 运行级别1.1.2 warm reset 寄存器1.1.3 PSCI Suspend/Resume ARM64 Reset ARMv8-A支持两种复位&#xff0c;冷复位和热复位。两者主要区别是冷复位会将debug 功能模块也一同复位&#xff0c;热复位在默认情况则不会复位 debug 功 能模块。 处理…

手机便签中可以打勾的圆圈或小方块怎么弄?

在日常的生活和工作中&#xff0c;很多网友除了使用手机便签来记录灵感想法、读书笔记、各种琐事、工作事项外&#xff0c;还会用它来记录一些清单&#xff0c;例如待办事项清单、读书清单、购物清单、旅行必备物品清单等。 在按照记录的清单内容来执行的时候&#xff0c;为了…

创建好的VMware虚拟机如何连接上外网?MobaX和XShell如何连接虚拟机

配置虚拟机网卡 首先点击VMware菜单栏&#xff0c;编辑-虚拟网络编辑器-更改设置 选择VMnet8-NAT设置&#xff0c;并记住子网IP之后有用 记住网关IP 修改实际创建的虚拟机网卡 修改设置&#xff0c;vi /etc/sysconfig/network-scripts/ifcfg-ens32 修改前&#xff1a; 修…

预测地震引发的洪水

当断层陡坡像水坝一样发挥作用时&#xff0c;地表破裂地震可能会突然改变河流的路线。研究人员现已成功模拟发生在新西兰的此类事件。 当断层位移突然改变地貌时&#xff0c;地表破裂地震可能会改变河流的路线&#xff0c;例如新西兰的怀奥托阿/克拉伦斯河&#xff0c;如图所示…

SpringBoot 的自动装配特性

1. Spring Boot 的自动装配特性 Spring Boot 的自动装配&#xff08;Auto-Configuration&#xff09;是一种特性&#xff0c;它允许您在应用程序中使用默认配置来自动配置 Spring Framework 的各种功能和组件&#xff0c;从而减少了繁琐的配置工作。通过自动装配&#xff0c;您…

算法40:移动零

一、需求 给定一个数组 nums&#xff0c;编写一个函数将所有 0 移动到数组的末尾&#xff0c;同时保持非零元素的相对顺序。 请注意 &#xff0c;必须在不复制数组的情况下原地对数组进行操作。 示例 1: 输入: nums [0,1,0,3,12] 输出: [1,3,12,0,0]示例 2: 输入: nums …