weex实现带有跟手动画的tab栏

2023-11-10

在weex开发的群中看到有人提到这个问题,就想着去实现以下,还不是很完美,只支持一屏的tab栏内容,后续会进行优化。

2019-6-20 更新:已支持滚动跟手,可以超出屏幕。

2019-6-23 更新:解决子元素包含滚动标签时无法滑动切换的问题。

2019-6-25 更新:修复底部和指示器会超出屏幕的bug。

效果图如下:

效果图

组件源码:

<template>
    <div class="container">
        <scroller scroll-direction="horizontal" show-scrollbar="false"show style="flex-direction: row;width: 750px;height:90px">
            <div class="tab_top">
                <div ref="tab_item" class="tab_item" @click="itemClick(index)"
                     :style="{width:itemWidth,backgroundColor:backgroundColor}" v-for="(item,index) in tabItems">
                    <text class="tab_item_text" :style="{fontSize:fontSize,
                    color:selectIndex===index?textSelectColor:textColor}">{{item.text}}</text>
                </div>
                <div ref="indicator" class="indicator" :style="{width:itemWidth-40,backgroundColor:indicatorColor}"></div>
            </div>
        </scroller>

        <div class="tab_content" @touchstart="bindingX" :prevent-move-event="true"
             :style="{width:tabItems.length*750}" ref="tab_content">
            <!--@touchstart="bindingX"-->
            <slot></slot>
        </div>

    </div>
</template>

<script>
    import Head from "./head.vue";
    let binding = weex.requireModule('binding');
    import { parse } from 'bindingx-parser';
    let modal=weex.requireModule('modal');
    let dom=weex.requireModule('dom')
    let animation=weex.requireModule('animation');
    export default {
        components: {Head},
        name: "tab-container",
        props:{
            tabItems:{
                default:
                  [{
                    text:"测试1",
                  }],
                type:Array
            },
            itemWidth:{
                default:200,
                type:Number
            },
            fontSize:{
                default:"28px",
                type:String
            },
            textSelectColor:{
                default:'#268cf0'
            },
            textColor:{
                default:'#333'
            },
            /*tab底部指针的偏移量*/
            offset:{
                default:250,
                type:Number
            },
            /*可以触发动画的偏移量*/
            dist:{
                default:250,
                type:Number
            },
            backgroundColor:{
                default:"#ffffff",
                type:String
            },
            indicatorColor:{
                default:"#17acf0",
                type:String
            },
            selectIndex:{
                default:0,
                type:Number
            },
            duration: {
                type: [Number, String],
                default: 150
            },
            timingFunction: {
                type: String,
                default: 'cubic-bezier(0.25, 0.46, 0.45, 0.94)'
            },
        },
        data(){
            return{
                dx:0,
                dx_i:0,
                animationIng:false,
                startScrollIndex:0
            }
        },
        watch:{
            selectIndex:function () {
                this.exeAnimationX()
            }
        },
        methods:{
            /*手势使用bindingX提高性能*/
            bindingX(element){
                let self=this;
                if(self.animationIng){
                    return;
                }
                let {dist}=this;
                let dx=-750*self.selectIndex;
                let dx_i=self.itemWidth*self.selectIndex;
                let indicator=self.$refs.indicator.ref;
                let tab_content=self.$refs.tab_content.ref;

                let tab_content_x=`x+${dx}>0?0:x+${dx}`;
                let indicator_x=`x+${dx}>0?0:${dx_i}-x/${750/this.itemWidth}`;
                if(-dx/750===self.tabItems.length-1){
                    indicator_x=`x<0?${dx_i}:${dx_i}-x/${750/this.itemWidth}`;
                    tab_content_x=`x<0?${dx}:x+${dx}`;
                }
                if(element.ref==null){
                    return;
                }
                self.token=binding.bind({
                    anchor:element.ref,
                    eventType:'pan',
                    props:[
                        {
                            element:tab_content,
                            property:'transform.translateX',
                            expression:parse(tab_content_x)
                        },
                        {
                            element:indicator,
                            property:'transform.translateX',
                            expression:parse(indicator_x)
                        }
                    ]
                },(event)=>{
                    // modal.alert({message:event});
                    let {deltaX,state}=event;

                    if(state==='end'){
                        if(deltaX>dist&&this.selectIndex>0){
                            this.selectIndex--;
                        }else if(deltaX<-dist&&this.selectIndex<self.tabItems.length-1){
                            this.selectIndex++;
                        }else{
                            this.exeAnimationX()
                        }
                    }
                }).token;
            },
            /*动画尽量使用animation简化代码*/
            exeAnimationX(){
                let self=this;
                self.animationIng=true;
                let tab_content=self.$refs.tab_content;
                let indicator=self.$refs.indicator;
                const { duration, timingFunction,selectIndex} = this;
                const dist =selectIndex * 750;
                const dist_i=self.itemWidth*selectIndex;
                animation.transition(tab_content, {
                    styles: {
                        transform: `translateX(${-dist}px)`
                    },
                    duration: duration,
                    timingFunction,
                    delay: 0
                }, () => {
                    self.$emit("itemClick",selectIndex);
                    self.animationIng=false;
                    if(self.offset<dist_i){
                        dom.scrollToElement(self.$refs.tab_item[selectIndex], {offset:-self.offset,animated:true})
                    }else{
                        dom.scrollToElement(self.$refs.tab_item[selectIndex], {offset:-dist_i,animated:true})
                    }
                    // binding.unbindAll();
                });
                animation.transition(indicator, {
                    styles: {
                        transform: `translateX(${dist_i}px)`
                    },
                    duration: duration,
                    timingFunction,
                    delay: 0
                }, () => {
                });
            },
            itemClick(index){
                 let self=this;
                if(self.animationIng){
                    return;
                }
                self.selectIndex=index;
                // dom.scrollToElement(this.$refs.tab_item[index], {offset:-self.offset,animated:true})
                // self.exeAnimationX(index);
            }
        },
        created(){
             let self=this;
             self.startScrollIndex=750/self.itemWidth;
             setTimeout(()=>{
                 // dom.scrollToElement(this.$refs.tab_item[self.selectIndex], {offset:-self.offset,animated:true})
                 self.exeAnimationX();
             },100);
        }
    }
</script>

<style scoped>
    .container{
        /*position:relative;*/
        /*top:0;*/
        /*left:0;*/
        /*bottom: 0;*/
        /*background-color: yellow;*/
    }
    .tab_content{
        flex-direction: row;
        position:relative;
        top:0;
        left:0;
        bottom: 0;
    }
    .tab_top{
        flex-direction: row;
    }
    .tab_item{
        height: 90px;
        align-items: center;
        justify-content: center;
    }
    .tab_item_text{
    }
    .tab_item_text_focus{
        font-size: 26px;
        color: #268cf0;
    }
    .indicator{
        height: 5px;
        background-color: white;
        position: absolute;
        bottom: 0;
        left: 20px;
    }
</style>

使用方法:

<template>
  <div>
     <head></head>
      <tab-container @itemClick="itemClick" :tabItems="tabItems">
           <list v-for="(item,index) in tabItems" class="tabCell">
                        //滚动元素需要把事件给到每个根子元素上,并设置prevent-move- 
                        //event="true" 
                        <cell class="content" :prevent-move-event="true" @horizontalpan="wxcPanItemPan">
              <text class="tab_item_text" style="font-size: 34px;color:#ffffff">{{item}}</text>
                     </cell >
           </list>
           //普通非滚动元素用法,不用传递事件解决冲突
           // <div class="content"  v-for="(item,index) in tabItems" >
           // <text class="tab_item_text" style="font-size: 34px;color:#ffffff">{{item}} 
           //  </text>
           // </div>

      </tab-container>

  </div>
 
</template>
<style>
    .content{
        width: 750px;
        height: 1200px;
        align-items: center;
        justify-content: center;
        background-color: green;
    }
</style>

<script>
  const modal=weex.requireModule('modal');
  import Head from "../components/head.vue";
  import TabContainer from "../components/tabContainer.vue";
  export default {
      components: {
          TabContainer,
          Head},
      data () {
      return {
        text: 'Hello World.',
          tabItems:[{
              text:"测试1",
              font_size:26,
              textColor:"#ffffff"
          },{
              text:"测试2",
              font_size:26,
              textColor:"#ffffff"
          },{
              text:"测试3",
              font_size:26,
              textColor:"#ffffff"
          },
              {
                  text:"测试4",
                  font_size:26,
                  textColor:"#ffffff"
              },
              {
                  text:"测试5",
                  font_size:26,
                  textColor:"#ffffff"
              },
              {
                  text:"测试6",
                  font_size:26,
                  textColor:"#ffffff"
              },
              {
                  text:"测试7",
                  font_size:26,
                  textColor:"#ffffff"
              }],
          contents:[111,222,333],
          param:''
      }
    }
    ,
      methods:{
          itemClick(index){
              modal.toast({message:index})
          },
          //这个事件是为了解决事件冲突
          horizontalpan(e) {
                // modal.alert({message:e});
                if (e.state === 'start') {
                    if (commonUtils.env.supportsEBForAndroid()) {
                        this.$refs['tabContainer'].bindingX(e.currentTarget);
                    }
                }

            },
      }
     ,
      created:function(){

      }
  }
</script>

我的weex相关博客:weex开发系列

我的开源weex模板项目:IWeex

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

weex实现带有跟手动画的tab栏 的相关文章

  • js判断Android、iOS或浏览器

    第一种 通过判断浏览器的userAgent 用正则来判断是否是ios和Android客户端 代码如下
  • vue-cli3项目中使用flexible和rem适配移动端的一些踩坑

    最近在做一个移动端的项目 需要做一些适配处理 其实以前一直都是浑水摸鱼式的 虽然看了很多关于移动端适配的方案 感觉都很厉害 但一直迟迟不曾实践 ps 好像大部分中小公司对这方面都没啥要求啊 在网上找了一番后 发现手淘的flexible re
  • 自定义Weex组件——Weex的学习之路(八)

    在四月份和五月份的时候我用业余时间来学习weex 在这期间一直在看文档写demo 每一个组件都自己写demo运行一遍 我本人是做Android开发的 对JS CSS和Html有一定的了解 所以学习weex不是很难 然后我把自己所学的主要经历
  • Android Studio 安装虚拟机报错 Intel® HAXM installation failed.

    Android Studio 安装虚拟机时报错 Intel HAXM installation failed To install Intel HAXM follow the instructions found at https gith
  • 【Flutter 3-4】Flutter进阶教程——数据持久化sqflite使用

    作者 弗拉德 来源 弗拉德 公众号 fulade me sqflite 数据持久化是在移动端开发中必不可少的技术手段 我们总是有一些用户信息 应用资源 列表数据等需要存储起来 这里我们主要来讲基于SQLite数据库的数据储存 SQLite
  • 【面试】移动端的自适应布局如何实现?

    1 百分比布局 使用百分比布局实现移动端的自适应布局 可以让页面元素随设备屏幕的大小自动适应缩放 从而实现不同设备的自适应 下面是具体的实现步骤 1 1 在CSS中 使用百分比作为元素的宽高 边距等属性值 例如 如果设计稿中某个元素的宽度为
  • flutter项目中使用

    一些小部件 GestureDetector 手势 手势表示由一个或多个指针移动组成的动作 主要有以下几种 onTap 点击事件触发 Divider 设置分割线 SharedPreferences数据存储 SharedPreferences
  • Weex页面的编写——Weex的学习之路(六)

    通过前几博客我们把weex的内置组件都学习完了 组件的单独使用想必都可以了 那么 这次我们来做weex实际页面的编写 见证一下 一套代码在Android Ios和H5上使用 在weex官网推荐我们使用Weex Studio作为编译器 其实这
  • 什么是弱网测试?为什么要进行弱网测试?怎么进行弱网测试?

    什么是弱网测试 由于处在移动互联网盛行的时代 网络形态除了有线连接外 还有2G 3G 4G Wifi 5G等多种手机网络连接方式 首先额外补充一些5G的知识 2分钟了解什么是5G 在前不久结束的 MWC 2018 上 5G 成了全球的一个热
  • 移动WEB开发之-REM(rem)布局

    目录 移动WEB开发之REM布局 rem基础 rem单位 媒体查询 什么是媒体查询 媒体查询语法规范 1 mediatype 查询类型 2 关键字 3 媒体特性 4 媒体查询书写规则 less 基础 维护css弊端 Less 介绍 Less
  • ios 设备的移动端页面 光标错位

    1 这个问题遇到好多次了 经常是上下错位 原因是在input 获取焦点以后 键盘弹出后 页面网上移动 但是光标停留在了以前的位置 如果页面不复杂 可以在body上添加 position fixed 但是这样会引起页面失去滚动能力 目前只能是
  • 移动端Touch (触摸)事件

    一 常见的触摸事件 touchstart touchmove和touchend touchstart事件 当手指触摸屏幕时候触发 即使已经有一个手指放在屏幕上也会触发 touchmove事件 当手指在屏幕上滑动的时候连续地触发 在这个事件发
  • vue项目CDN引入

    注 一些对系统并不是至关重要的文件诸如字体文件 css重置 js小插件 背景图片等可能适合使用CDN 因为即使CDN文件不可用 网站也能做到平稳退化 第一步 public index html 里 注 需引入vue并且main js中正常引
  • vue移动端无法使用string.replaceAll,报错显示空白

    开发vue时 使用了replaceAll函数 在pc端调试没问题 但是打包部署到移动端端测试时 发现部分页面显示空白 控制台只显示Error 经过排查是replaceAll函数报错了 替换成replace解决
  • ios 固定定位 input获取焦点,ios 滚动条滚动 fixed固定定位失效,位置偏移

    http efe baidu com blog mobile fixed layout 还发现一个问题就是ios input设置readonly 还是能看到光标 然后解决方法是在行内写了 nf cus this blur
  • 移动端适配dpr

    1 移动端适配的代码 设计稿iPhone6 如下 function doc win seMetaTagScale doc win var fn function var deviceWidth doc documentElement cli
  • 移动端浏览器兼容性问题

    在开发移动端商城项目的时候 在实现功能和处理bug的过程中出现了一些之前没有见过的问题 在这里记录一下 1 IOS移动端click事件300ms的延迟响应 移动设备上的web网页是有300ms延迟的 玩玩会形成按钮点击延迟甚至是点击失效 这
  • 系统时间显示踩坑记录

    问题 签到前时间每秒变化显示 原始做法是 截取获取的系统时间的后九位 但是红米手机的系统时间最后两位不是秒 而是上下午的英文字母 导致小时截取不到 如图中间位置 希望实现如图左 方法 分别获取系统的时分秒 处理下不是两位的数字 然后拼接起来
  • 基于vue实现移动端点击上方导航,内容滑动切换,滑动内容导航自动切换。

    这是在日常开发过程中常见的选项卡 带滑动切换效果 小白一枚 不足之处还望体谅 包涵 这也是第一次尝试写博客 以后会继续分享一些工作中的问题与收获 实现效果 点击上方导航 当前导航添加样式 下方内容滑动切换 滑动下方内容上面导航切换 第一步
  • ADB:实现双击效果

    命令 adb shell seq 2 while read i do input tap 993 814 input tap 993 814 sleep 0 01 done 但是 上述命令有时候变成两次点击 成功率大概80 左右把 没办法

随机推荐

  • iOS编程基础-OC(七)-运行时系统(续)

    该系列文章系个人读书笔记及总结性内容 任何组织和个人不得转载进行商业活动 第7章 运行时系统 7 4 动态绑定 动态绑定 dynamic binding 是指在运行程序时 而不是在编译时 将消息与方法对应起来的处理过程 在运行程序和发送消息
  • Motionbuilder矩阵计算方式

    基本使用 对于类型为 FBModel 的对象 有 GetMatrix SetMatrix 方法来获取及设置其变换矩阵 GetMatrix pMatrix FBMatrix pWhat kModelTransformation pGlobal
  • SpringBoot 集成 Apollo 配置中心,一文搞定!(万字长文)

    由于 Apollo 概念比较多 刚开始使用比较复杂 最好先过一遍概念再动手实践尝试使用 1 背景 随着程序功能的日益复杂 程序的配置日益增多 各种功能的开关 参数的配置 服务器的地址 对程序配置的期望值也越来越高 配置修改后实时生效 灰度发
  • STM32操作增量式编码器(一)----使用外部中断实现测速

    1 编码器概述 这里对此不再详细说明 本博文重在如何使用编码器 有兴趣的同学可以去网上了解 或者参考一下博文 旋转编码器工作原理 2 增量式编码器控制思路 图2 1 编码器实物图 图2 2 编码器与MCU接线图 我们首先需要清楚编码器输出什
  • 【剑指Offer题解:java】从上往下打印二叉树

    题目 从上往下打印出二叉树的每个节点 同层节点从左至右打印 分析 初始化 一个队列Queue queue 将root节点入队列queue 如果队列不空 做如下操作 弹出队列头 保存为node 将node的左右非空子节点加入队列 做2 3步骤
  • Zimbra安装成功后,邮件发送失败!!急!!发生错误 (mail.TRY_AGAIN),原因不详。

    method unknown msg try again Unable to connect to the MTA code mail TRY AGAIN detail soap Receiver trace com zimbra cs m
  • Less-27and27a

    文章目录 1 思路分析 2 注入过程 3 27a 1 思路分析 这一关表上上告诉你他只是过滤了union和select 其实不然 function blacklist id id preg replace id strip out id p
  • MQ-2烟雾报警器

    MQ 2烟雾报警器 原理 MQ 2型烟雾传感器属于二氧化锡半导体气敏材料 属于表面离子式N型半导体 处于200 300摄氏度时 二氧化锡吸附空气中的氧 形成氧的负离子吸附 使半导体中的电子密度减少 从而使其电阻值增加 当与烟雾接触时 如果晶
  • 传输线阻抗理论

    一 理想元件阻抗特性 对于所有的理想元件 传输线 阻抗 为该导体两端的电压和流经该导体的电流的比值 一般包括阻抗 感抗和容抗的统称 电阻阻抗 电感感抗 电容容抗 显然 对于理想电感和电容 其阻抗和频率有关 理想电感器的阻抗随频率升高而增大
  • maven手动引入仓库文件操作

    捕获 jpg 一 idea打开maven命令窗口 在框里输入命令 mvn install install file DgroupId com elink web DartifactId jcifs Dversion 1 3 15 SNAPS
  • 【2021年全国大学生数学建模竞赛题】“生产企业原材料的订购与运输”详细解析(内附MATLAB代码)

    2021年全国大学生数学建模竞赛题 生产企业原材料的订购与运输 详细解析 内附MATLAB代码 文章目录 1 模型建立 1 1确定被评判对象的对象集及因素集 1 2确定各评价指标权重 1 3建立相对模糊及对因素的偏差加权平均 1 4根据Fj
  • Android APK安装完成自动删除安装包

    需要实现此功能 一般实际开发是在自动版本更新上 当更新完开始自动安装完毕后 删除内存卡里的安装包 实现方式很简单 监听应用广播 获取内存卡下的文件 删除 1 监听广播 java view plain copy package com exa
  • 数据库系统原理课程总结8——备份与日志初步、并发模拟实验

    一 备份与日志初步实验 1 了解你所使用的数据库平台的单表数据备份和整库备份方法 进行相应备份操作 并尝试利用备份数据在另一个机器上恢复数据 并在实验报告中描述上述过程 答 首先 在MySQL中使用mysqldump将数据库的单表数据以sq
  • Hutool(Excel工具使用)

    Hutool Excel工具使用 官方文档Hutool 目录 基本依赖的导入 Writer方法的使用 1 1 写出List数据 1 2 写出Map数据 1 3 写出我们的Bean对象 1 4 自定义Bean的key别名 1 5 写出到IO流
  • LeetCode-116.填充每个节点的下一个右侧节点指针、深度优先搜索

    题目分析 广度优先搜索 题目要求把二叉树中每一层的的节点连起来 最简单的方法即 BFS 按层的顺序的对树进行遍历 但需要使用 queue 数据结构 空间复杂度为 O N 不符合题目要求 深度优先搜索 由于 next 指针的存在 可以实现对二
  • Unity WorldToScreenPoint坐标变换

    功能 实现标签跟随物体运动 标签是一个Prefab 由底图和文字组成 Dota2中英雄血条的实现也是这种原理 说到底就是标签根据物体位置不间断刷新自己的坐标值 3D gt 2D gt 3D 先来了解一下Unity D中的坐标系统 1 Wor
  • 前台页面上传data image图片,java后台接收图片保存

    最近在项目中有这么一个需求 就是上传一个视频文件 然后要获取视频文件的第一帧图片 这个可以通过canvas获取得到 得到的是一个dataURL 之后还要将这个图片上传到云 这个时候如何操作就不清楚了 于是乎 google一番 总结如下 将d
  • Redis源码分析(三)—— 字典的设计与实现

    前言 字典是一种用于保存键值对的数据结构 Redis数据库使用字典做为底层实现 字典也是哈希键的底层实现之一 C语言中并没有内置字典这个数据结构 Redis自己实现了字典 以下结合源码分析Redis字典的设计与实现 源码版本 Redis 6
  • java执行linux命令:head -n 80 /dev/urandom

    看了微信小程序api后 发现登录Logo接口需要处理随机key 所以着手处理了一下 直接贴代码 先运行命令 让其生成168位随机数 private static String wxSessionkey F3UENUg3JcI31O2RpoB
  • weex实现带有跟手动画的tab栏

    在weex开发的群中看到有人提到这个问题 就想着去实现以下 还不是很完美 只支持一屏的tab栏内容 后续会进行优化 2019 6 20 更新 已支持滚动跟手 可以超出屏幕 2019 6 23 更新 解决子元素包含滚动标签时无法滑动切换的问题