小程序弹出层/抽屉封装 (抖音小程序)

news2025/5/20 15:54:50

最近忙于开发抖音小程序,最想吐槽的就是,既没有适配的UI框架,百度上还找不到关于抖音小程序的案列,我真的很裂开啊,于是我通过大模型封装了一套代码

效果如下

在这里插入图片描述

介绍

可以看到 这个弹出层是支持关闭和标题显示的,同时这个出场肯定是从下到上的,当然它肯定是支持任意方向弹出的 效果类似于element ui 的抽屉效果

代码

在 components 新建组件 drawer

抽屉的结构部分

<!-- drawer.wxml -->
<view class="drawer-container" tt:if="{{isVisible}}">
    <view class="drawer-mask" style="{{maskStyle}}" bindtap="onMaskClick" tt:if="{{mask}}"></view>
    <view class="drawer-content" style="{{drawerStyle}}" catchtouchmove="stopPropagation">
        <!-- 头部区域 -->
        <view class="drawer-header" tt:if="{{showTitle || showClose}}">
            <text class="drawer-title" tt:if="{{showTitle && title}}">{{title}}</text>
            <view class="header-spacer" tt:if="{{!title && showClose}}"></view>
            <view class="drawer-close-btn" tt:if="{{showClose}}" bindtap="onCloseClick">
                <text class="close-icon">×</text>
            </view>
        </view>

        <!-- 内容区域 -->
        <view class="drawer-body">
            <slot></slot>
        </view>
    </view>
    </drawer-container>

抽屉逻辑处理部分

// drawer.js
Component({
  properties: {
    show: {
      type: Boolean,
      value: false,
      observer: 'toggleDrawer'
    },
    direction: {
      type: String,
      value: 'bottom', // left, right, top, bottom
      observer: 'updatePosition'
    },
    width: {
      type: String,
      value: '100%'
    },
    height: {
      type: String,
      // value: '100%'
    },
    mask: {
      type: Boolean,
      value: true
    },
    maskClosable: {
      type: Boolean,
      value: true
    },
    showClose: {
      type: Boolean,
      value: true
    },
    title: {
      type: String,
      value: ''
    },
    showTitle: {
      type: Boolean,
      value: true
    },
    duration: {
      type: Number,
      value: 300
    }
  },
  
  data: {
    drawerStyle: '',
    maskStyle: '',
    isVisible: false,
    isTransitioning: false
  },
  
  methods: {
    toggleDrawer(newVal) {
      if (newVal) {
        this.openDrawer();
      } else {
        this.closeDrawer();
      }
    },
    
    updatePosition() {
      if (!this.data.isVisible) {
        this.setInitialPosition();
      }
    },
    
    setInitialPosition() {
      const { direction, width, height } = this.properties;
      let style = `position: fixed; z-index: 10000;`;
      
      switch(direction) {
        case 'left':
          style += `width: ${width}; height: ${height}; top: 0; left: 0; transform: translateX(-100%);`;
          break;
        case 'right':
          style += `width: ${width}; height: ${height}; top: 0; right: 0; transform: translateX(100%);`;
          break;
        case 'top':
          style += `width: ${width}; height: ${height}; top: 0; left: 0; transform: translateY(-100%);`;
          break;
        case 'bottom':
          style += `width: ${width}; height: ${height}; bottom: 0; left: 0; transform: translateY(100%);`;
          break;
      }
      
      this.setData({
        drawerStyle: style
      });
    },
    
    openDrawer() {
      if (this.data.isVisible || this.data.isTransitioning) return;
      
      const { direction, duration } = this.properties;
      
      // 先设置初始位置并显示
      this.setInitialPosition();
      this.setData({
        isVisible: true,
        maskStyle: 'opacity: 0;'
      });
      
      // 强制重排后应用动画
      setTimeout(() => {
        let drawerStyle = this.data.drawerStyle;
        const transformProp = direction.includes('left') || direction.includes('right') ? 'translateX' : 'translateY';
        drawerStyle = drawerStyle.replace(`${transformProp}(-100%)`, `${transformProp}(0)`);
        drawerStyle = drawerStyle.replace(`${transformProp}(100%)`, `${transformProp}(0)`);
        drawerStyle += `transition: transform ${duration}ms cubic-bezier(0.25, 0.8, 0.25, 1);`;
        
        this.setData({
          drawerStyle,
          maskStyle: `opacity: 0.7; transition: opacity ${duration}ms cubic-bezier(0.25, 0.8, 0.25, 1);`
        });
        
        this.data.isTransitioning = true;
        
        setTimeout(() => {
          this.data.isTransitioning = false;
          this.triggerEvent('open');
        }, duration);
      }, 10);
    },
    
    closeDrawer() {
      if (!this.data.isVisible || this.data.isTransitioning) return;
      
      const { direction, duration } = this.properties;
      let drawerStyle = this.data.drawerStyle;
      const transformProp = direction.includes('left') || direction.includes('right') ? 'translateX' : 'translateY';
      
      // 添加关闭动画
      drawerStyle = drawerStyle.replace(`${transformProp}(0)`, `${transformProp}(${direction.includes('left') || direction.includes('top') ? '-100%' : '100%'})`);
      drawerStyle += `transition: transform ${duration}ms cubic-bezier(0.25, 0.8, 0.25, 1);`;
      
      this.setData({
        drawerStyle,
        maskStyle: `opacity: 0; transition: opacity ${duration}ms cubic-bezier(0.25, 0.8, 0.25, 1);`
      });
      
      this.data.isTransitioning = true;
      
      // 动画结束后隐藏
      setTimeout(() => {
        this.setData({
          isVisible: false
        });
        this.data.isTransitioning = false;
        this.triggerEvent('close');
      }, duration);
    },
    
    onMaskClick() {
      if (this.properties.maskClosable && !this.data.isTransitioning) {
        this.closeDrawer();
      }
    },
    
    onCloseClick() {
      if (!this.data.isTransitioning) {
        this.closeDrawer();
      }
    },
    
    stopPropagation() {}
  },
  
  attached() {
    this.setInitialPosition();
  }
});

抽屉的基本样式

/* drawer.wxss */
.drawer-container {
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    z-index: 9999;
}

.drawer-mask {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background-color: rgba(0, 0, 0, 0.6);
    backdrop-filter: blur(2px);
}

.drawer-content {
    position: fixed;
    background-color: #fff;
    box-shadow: -2px 0 15px rgba(0, 0, 0, 0.1);
    overflow: hidden;
    border-top-left-radius: 20rpx;
    border-top-right-radius: 20rpx;
    -webkit-overflow-scrolling: touch;
}

/* 右侧抽屉的阴影 */
.drawer-content[style*="right: 0"] {
    box-shadow: -2px 0 15px rgba(0, 0, 0, 0.1);
}

/* 左侧抽屉的阴影 */
.drawer-content[style*="left: 0"][style*="width"] {
    box-shadow: 2px 0 15px rgba(0, 0, 0, 0.1);
}

/* 顶部抽屉的阴影 */
.drawer-content[style*="top: 0"][style*="height"] {
    box-shadow: 0 2px 15px rgba(0, 0, 0, 0.1);
}

/* 底部抽屉的阴影 */
.drawer-content[style*="bottom: 0"][style*="height"] {
    box-shadow: 0 -2px 15px rgba(0, 0, 0, 0.1);
}



.drawer-title {
    font-size: 32rpx;
    font-weight: 500;
    color: #333;
}


.drawer-close-btn:hover {
    background-color: rgba(0, 0, 0, 0.1);
}

.close-icon {
    font-size: 36rpx;
    color: #333;
}

/* 内容区域 */
.drawer-body {
    height: calc(100% - 100rpx);
    /* 减去头部高度 */
    overflow-y: auto;
    padding: 40rpx;
    box-sizing: border-box;
}


/* drawer.wxss 新增样式 */
.drawer-header {
    position: relative;
    padding: 30rpx 40rpx;
    min-height: 100rpx;
    box-sizing: border-box;
    display: flex;
    align-items: center;
    justify-content: space-between;
    border-bottom: 1rpx solid #eee;
}

.header-spacer {
    flex-grow: 1;
}

.drawer-close-btn {
    position: absolute;
    top: 30rpx;
    right: 40rpx;
    width: 48rpx;
    height: 48rpx;
    border-radius: 50%;
    background-color: rgba(0, 0, 0, 0.05);
    display: flex;
    align-items: center;
    justify-content: center;
    transition: background-color 0.2s;
    z-index: 10;
}

对应文件的json引入使用

{
    "navigationBarTitleText": "商品详情",
    "usingComponents": {
        "Drawer": "/components/Drawer/Drawer"
    },
    "allowsBounceVertical": "NO"
}

界面部分


<Drawer show="{{dateShow}}" showTitle="{{true}}" showClose="{{true}}" title="账单详情" class="detail-popup" bind:close="handlePopupClose">
    <view class="repayment-popup">
        <view class="repayment-popup-item">
            <text>账期</text>
            <text>第{{repaymentTime.periods}}期</text>
        </view>
        <view class="repayment-popup-item">
            <text>账单日</text>
            <text>{{util.formatDateToDay(repaymentTime.date)}}</text>
        </view>
        <view class="repayment-popup-item">
            <text>账单总额</text>
            <text>{{util.moneyFormatMinZero(repaymentTime.waitPay)}}</text>
        </view>
        <view class="repayment-popup-item">
            <text>待还本金</text>
            <text>{{util.moneyFormatMinZero(repaymentTime.waitPrincipalMoney)}}</text>
        </view>
        <view class="repayment-popup-item">
            <text>待还滞纳金</text>
            <text>{{util.moneyFormatMinZero(repaymentTime.waitOverdueMoney)}}</text>
        </view>
    </view>
</Drawer>

界面逻辑部分

const app = getApp();
Page({
	data: {
		dateShow: false,
	},
	 appreciationEvent() {
        this.setData({
            dateShow: true,
        });
    },

    handlePopupClose() {
        this.setData({
            dateShow: false
        });
    }
		

完结啦 🎉 🎉 🎉

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

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

相关文章

电子电路原理第十六章(负反馈)

1927年8月,年轻的工程师哈罗德布莱克(Harold Black)从纽约斯塔顿岛坐渡轮去上班。为了打发时间,他粗略写下了关于一个新想法的几个方程式。后来又经过反复修改, 布莱克提交了这个创意的专利申请。起初这个全新的创意被认为像“永动机”一样愚蠢可笑,专利申请也遭到拒绝。但…

命令拼接符

Linux多命令顺序执行符号需要记住5个 【&#xff5c;】【||】【 ;】 【&】 【&&】 &#xff0c;在命令执行里面&#xff0c;如果服务器疏忽大意没做限制&#xff0c;黑客通过高命令拼接符&#xff0c;可以输入很多非法的操作。 ailx10 网络安全优秀回答者 互联网…

【通用智能体】Lynx :一款基于终端的纯文本网页浏览器

Lynx &#xff1a;一款基于终端的纯文本网页浏览器 一、Lynx简介二、应用场景及案例场景 1&#xff1a;服务器端网页内容快速查看场景 2&#xff1a;网页内容快速提取场景 3&#xff1a;表单提交与自动化交互场景 4&#xff1a;网络诊断与调试场景 5&#xff1a;辅助工具适配 三…

51单片机的lcd12864驱动程序

#include <reg51.h> #include <intrins.h>#define uchar

GStreamer (三)常⽤插件

常⽤插件 1、Source1.1、filesrc1.2. videotestsrc1.3. v4l2src1.4. rtspsrc和rtspclientsink 2、 Sink2.1. filesink2.2. fakesink2.3. xvimagesink2.4. kmssink2.5. waylandsink2.6. rkximagesink2.7. fpsdisplaysink 3 、视频推流/拉流3.1. 本地推流/拉流3.1.1 USB摄像头3.1…

软件架构风格系列(2):面向对象架构

文章目录 引言一、什么是面向对象架构风格1. 定义与核心概念2. 优点与局限性二、业务建模&#xff1a;用对象映射现实世界&#xff08;一&#xff09;核心实体抽象1. 员工体系2. 菜品体系 &#xff08;二&#xff09;封装&#xff1a;隐藏实现细节 三、继承实战&#xff1a;构建…

go-zero(十八)结合Elasticsearch实现高效数据检索

go-zero结合Elasticsearch实现高效数据检索 1. Elasticsearch简单介绍 Elasticsearch&#xff08;简称 ES&#xff09; 是一个基于 Lucene 库 构建的 分布式、开源、实时搜索与分析引擎&#xff0c;采用 Apache 2.0 协议。它支持水平扩展&#xff0c;能高效处理大规模数据的存…

AM32电调学习解读九:ESC上电启动关闭全流程波形分析

这是第九篇&#xff0c;前面的文章把各个模块的实现都介绍了一轮&#xff0c;本章是从运行的角度结合波形图&#xff0c;把整个流程走一遍。 先看下一运行的配置&#xff0c;我把一些配置关闭了&#xff0c;这样跑起来会好分析一些&#xff0c;不同配置跑起来效果会有差异。使用…

【notes】VScode 使用总结

文章目录 扩展 c/cwindows7 系统下 c/c 自动升级导致的插件无法正常使用 设置 文件格式设置打开文件的默认格式 扩展 c/c windows7 系统下 c/c 自动升级导致的插件无法正常使用 问题 1. c/c扩展的1.25.x版本不再支持windows7 系统&#xff0c;当设置VScode自动升级拓展插件时…

【论文阅读】KIMI K1.5: SCALING REINFORCEMENT LEARNING WITH LLMS

KIMI K1.5: SCALING REINFORCEMENT LEARNING WITH LLMS Scaling的解释&#xff1a; 通过系统性的方法扩展强化学习算法的能力&#xff0c;使其能够处理更复杂的问题、更大的状态/动作空间、更长的训练周期或更高效的资源利用 原文摘要&#xff1a; 研究背景与问题定位 传统预训…

Qwen3 - 0.6B与Bert文本分类实验:深度见解与性能剖析

Changelog [25/04/28] 新增Qwen3-0.6B在Ag_news数据集Zero-Shot的效果。新增Qwen3-0.6B线性层分类方法的效果。调整Bert训练参数&#xff08;epoch、eval_steps&#xff09;&#xff0c;以实现更细致的观察&#xff0c;避免严重过拟合的情况。 TODO&#xff1a; 利用Qwen3-0.6…

UWB定位方案在水力发电站人员安全的应用推荐

一、行业应用背景‌ 水力发电站具有‌环境复杂‌&#xff08;金属设备密集、高温高压区域多&#xff09;、‌安全风险高‌&#xff08;人员误入高危区域易引发事故&#xff09;等特点&#xff0c;传统定位技术难以满足精度与可靠性要求。品铂科技基于UWB的高精度定位系统已在多…

无刷直流水泵构成及工作原理详解--【其利天下技术】

无刷直流水泵是相对于有刷直流泵而言的。 一&#xff1a;无刷直流水泵简介 无刷直流水泵即BLDC PUMP&#xff0c;其中“BL”意为“无刷”&#xff0c;DC即直流电机。 无刷直流水泵(BLDC PUMP)以电子换向器取代了机械换向器&#xff0c;所以无刷直流水泵既具有直流电机良好的调…

大数据:新能源汽车宇宙的未来曲率引擎

** 发布日期&#xff1a;2025-05-14** 关键词&#xff1a;大数据、新能源、机器学习、碳中和、CSDN爆款 1. 大数据科普&#xff1a;定义、特征与技术核心 1.1 什么是大数据&#xff1f; 大数据&#xff08;Big Data&#xff09;指规模巨大、类型多样、生成速度快且价值密度低…

【Java ee】关于抓包软件Fiddler Classic的安装与使用

Web Debugging Proxy Tool | Fiddler Classic 安装网站↑ 下载好安装包之后&#xff0c;双击一路next就可以了 一、抓包软件 电脑上安装了抓包软件之后&#xff0c;抓包软件就可以监听你的网卡上通过的数据。 本来是你的客户端通过网卡&#xff0c;把数据发给目标服务器&a…

C++--内存管理

内存管理 1. C/C内存分布 在C语言阶段&#xff0c;常说局部变量存储在栈区&#xff0c;动态内存中的数据存储在堆区&#xff0c;静态变量存储在静态区&#xff08;数据段&#xff09;&#xff0c;常量存储在常量区&#xff08;代码段&#xff09;&#xff0c;其实这里所说的栈…

TC3xx学习笔记-UCB BMHD使用详解(二)

文章目录 前言Confirmation的定义Dual UCB: Confirmation StatesDual UCB: Errored State or ECC Error in the UCB Confirmation CodesECC Error in the UCB ContentDual Password UCB ORIG and COPY Re-programming UCB_BMHDx_ORIG and UCB_BMHDx_COPY (x 0-3)BMHD Protecti…

用Python实现数据库数据自动化导出PDF报告:从MySQL到个性化文档的全流程实践

本文将介绍如何使用Python构建一个自动化工具&#xff0c;实现从MySQL数据库提取员工数据&#xff0c;并为每位员工生成包含定制化表格的PDF报告。通过该方案&#xff0c;可显著提升数据导出效率&#xff0c;避免手动操作误差&#xff0c;同时支持灵活的格式定制。 需求&#…

实战设计模式之状态模式

概述 作为一种行为设计模式&#xff0c;状态模式允许对象在其内部状态改变时&#xff0c;改变其行为。这种模式通过将状态逻辑从对象中分离出来&#xff0c;并封装到独立的状态类中来实现。每个状态类代表一种特定的状态&#xff0c;拥有自己的一套行为方法。当对象的状态发生变…

什么是着色器 Shader

本人就是图形学结课了&#xff0c;对 OpenGL着色器还有很多疑问嘿嘿 文章目录 为什么要有着色器vshaderfshader 本文围绕 vshader 和 fshader 代码示例讲解。 &#xff08;着色器代码取自本人简单OpenGL项目 https://github.com/DBWGLX/-OpenGL-3D-Lighting-and-Shadow-Modeli…