CSS实现优惠券透明圆形镂空打孔效果等能力学习

news2024/11/15 10:13:19

前言:无他,仅供学习记录,通过一个简单的优惠券Demo实践巩固CSS知识。

本次案例主要学习或巩固一下几点:

  1. 实现一个简单的Modal;
  2. 如何进行复制文本到粘贴板;
  3. 在不使用UI的svg图片的情况下,如何用CSS实现类优惠券打孔的样式;
  4. createPortal的使用实践;

优惠券例子

  • 分上中下三层,父层级不设置底色,上下两层设置底色;
  • 中间打孔那层,定高度(如48px),不设置任何底色,使镂空穿透,且超出隐藏;
  • 打孔那层,然后通过伪元素before和after,设置相同宽高,描圆;
  • 打孔那层中间部分,通过border设置底色进行填充,然后通过定位使其到两边即可;
  • 至于分割线就比较简单了,实现方式很多,这里不作解析;

在这里插入图片描述

代码

index.tsx

import { Button } from 'antd-mobile';
import { createPortal } from 'react-dom';
import { useCallback, useState } from 'react';
import { copyText } from '@/utils';
import styles from './index.module.less';

interface CouponProps {
  couponData?: {
    couponName: string;
    couponCode: string;
    expirationDate: string;
    threshold: string;
  };
}

const Coupon = (props: CouponProps) => {
  const {
    couponName = '优惠券名称',
    couponCode = '1234567890',
    expirationDate = '2024-10-10 10:10:10',
    threshold = '部分男鞋用品可用'
  } = props?.couponData || {};
  const [visible, setVisible] = useState<boolean>(false);

  const open = useCallback(() => {
    setVisible(true);
    document.body.style.overflow = 'hidden'; // 禁用滚动
  }, []);

  const close = useCallback(() => {
    setVisible(false);
    document.body.style.overflow = 'auto'; // 恢复滚动
  }, []);

  return (
    <>
      <Button color='primary' onClick={open}>
        查看优惠券
      </Button>
      {visible
        ? createPortal(
            <div className={styles.myCouponDialog}>
              <div className={styles.mask} onClick={close} />
              <div className={styles.dialogContent}>
                <div className={styles.couponHeader}>
                  <div className={styles.couponName}>{couponName}</div>
                  <div className={styles.couponCode}>
                    <span>券码:{couponCode}</span>
                    <span onClick={() => copyText(couponCode)} className={styles.btn}>
                      复制
                    </span>
                  </div>
                </div>
                <div className={styles.couponCenter}>
                  <div />
                </div>
                <div className={styles.couponBottom}>
                  <div style={{ marginBottom: '6px' }}>
                    <span>有效期: </span>
                    <span>{expirationDate}</span>
                  </div>
                  <div>
                    <span>使用门槛: </span>
                    <span>{threshold}</span>
                  </div>
                </div>
              </div>
            </div>,
            document.getElementById('root') || document.body
          )
        : null}
    </>
  );
};

export default Coupon;

index.module.less

.myCouponDialog {
  z-index: 999;
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  display: flex;
  justify-content: center;
  align-items: center;
  overflow: hidden;

  .mask {
    z-index: 1;
    position: absolute;
    background-color: rgba(0, 0, 0, 0.5);
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
  }

  .dialogContent {
    z-index: 2;
    width: 80vw;
    border-radius: 4px;
    overflow: hidden;
    box-sizing: border-box;

    .couponHeader {
      padding: 6px 24px;
      background-color: #fff;

      .couponName {
        font-size: 18px;
        font-weight: 600;
        display: flex;
        justify-content: center;
        align-items: center;
        padding: 24px 0;
      }

      .couponCode {
        display: flex;
        align-items: center;
        justify-content: space-between;
        padding: 6px 16px;
        border: 1px solid rgba(0, 0, 0, 0.3);
        border-radius: 4px;
        margin-top: 4px;

        .btn {
          color: #06b752;
        }
      }
    }

    .couponCenter {
      width: 100%;
      height: 48px;
      position: relative;
      overflow: hidden;

      & > div {
        position: absolute;
        top: 50%;
        left: 18px;
        right: 18px;
        z-index: 2;
        height: 0;
        border-bottom: 1px dotted rgba(0, 0, 0, 0.5);
      }

      &::before,
      &::after {
        z-index: 1;
        content: '';
        border: calc(50vw - 18px) solid #fff;
        position: absolute;
        width: 36px;
        height: 36px;
        border-radius: 50%;
        top: 50%;
      }

      &::before {
        left: -50vw;
        transform: translateY(-50%);
      }

      &::after {
        right: -50vw;
        transform: translateY(-50%);
      }
    }

    .couponBottom {
      background-color: #fff;
      padding: 0 24px 24px;

      & > div {
        display: flex;
        align-items: center;
        justify-content: space-between;

        & > span:first-child {
          color: rgba(0, 0, 0, 0.5);
        }
      }
    }
  }
}

utils

import { Toast } from 'antd-mobile';

/**
 * 复制文本
 * @param text
 * @returns
 */
export const copyText = (text: string) => {
  return new Promise(resolve => {
    Toast.show({ content: '复制成功' });
    if (navigator.clipboard?.writeText) {
      return resolve(navigator.clipboard.writeText(text));
    }
    // 创建输入框
    const textarea = document.createElement('textarea');
    document.body.appendChild(textarea);
    // 隐藏此输入框
    textarea.style.position = 'absolute';
    textarea.style.clip = 'rect(0 0 0 0)';
    // 赋值
    textarea.value = text;
    // 选中
    textarea.select();
    // 复制
    document.execCommand('copy', true);
    textarea.remove();
    return resolve(true);
  });
};

效果

备注:样式个人随便搞的,不必理会

在这里插入图片描述

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

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

相关文章

【C++】模板特化

目录 一、非类型模板参数 二、模板的特化 &#x1f31f;概念 扩展小知识补充(1)&#xff1a; 扩展小知识补充(2)&#xff1a; &#x1f31f;函数模板特化 扩展小知识&#xff1a; &#x1f31f;类模板特化 ✨全特化 ✨偏特化 • 部分特化&#xff1a;将模板参数表中…

前端几种常见框架【第一节】

​ 大家好&#xff0c;我是程序员小羊&#xff01; 前言&#xff1a; 最近比较忙&#xff0c;本人在复习软考中级设计考试&#xff0c;所以本系列文从零基础开始复习软考到结束软考&#xff08;计算机技术与软件专业技术资格考试&#xff09;作为国家级职业资格认证考试&#x…

ROS2 2D相机基于AprilTag实现3D空间定位最简流程

文章目录 前言驱动安装下载安装方式一&#xff1a;方式二&#xff1a; 相机检测配置config文件编译、运行程序注意 内参标定标定板运行程序 apriltag空间定位标签打印下载安装可视化结果 前言 AprilTag是一种高性能的视觉标记系统&#xff0c;广泛应用于机器人导航、增强现实和…

简述CCS平面线性光源

光源在机器视觉系统中起着重要作用&#xff0c;不同环境、场景及应用合适光源都不一样&#xff0c;今天我们来看看LFX3-PT系列平面线性光源。它是最适合检测镜面物体的凹凸,外壳小巧的光源。备有根据检测条件可选的2种线间距。1mm型&#xff08;型号末尾&#xff1a;A&#xff…

【ArcGIS Pro第一期】界面简介

ArcGIS Pro简介 ArcGIS Pro界面简介1.1 打开工程1.2 使用功能区上的工具 参考 ArcGIS Pro 是一种基于功能区的应用程序。 ArcGIS Pro 窗口顶部的功能区有许多命令可供选择&#xff0c;而根据需要打开的各个窗格&#xff08;可停靠窗口&#xff09;中则提供了更为高级或专用的功…

erlang学习:用ETS和DETS存储数据

作用 ets和dets是两个系统模块&#xff0c;可以用来高效存储海量的Erlang数据。 ETS和DETS执行的任务基本相同&#xff1a;它们提供大型的键值查询表。ETS常驻内存&#xff0c;DETS则常驻磁盘。ETS是相当高效的&#xff1a;可以用它存储海量的数据&#xff08;只要有足够的内…

ACM模式 输入输出练习

牛客-练习地址 第一题 let cnt readline(); while(cnt--){let input readline()let arr input.split( ).map(Number)console.log(arr[0]arr[1]) }第二题 let cnt readline(); while(cnt--){let input readline()let arr input.split( ).map(Number)console.log(arr[0]ar…

Web攻防之应急响应(二)

目录 前提 &#x1f354;学习Java内存马前置知识 内存马 内存马的介绍 内存马的类型众多 内存马的存在形式 Java web的基础知识&#xff1a; Java内存马的排查思路&#xff1a; &#x1f354;开始查杀之前的需要准备 1.登录主机启动服务器 2.生成jsp马并连接成功 …

vivado 创建时间约束1

步骤3&#xff1a;创建时间约束 在此步骤中&#xff0c;您打开合成的设计并使用AMD Vivado™定时约束 男巫定时约束向导分析门级网表并发现缺失 约束。使用“定时约束”向导为此设计生成约束。 1.在“流导航器”中&#xff0c;单击“打开综合设计”。 2.当综合设计打开时&#…

六、MySQL高级—架构介绍(1)

&#x1f33b;&#x1f33b; 目录 一、Mysql 简介1.1 概述1.2 Mysql 高手是怎样炼成的 二、Mysql Linux 版的安装2.1 mysql5.52.2 mysql5.7 三、Mysql 的用户与权限管理3.1 MySQL的用户管理3.2 权限管理3.3 通过工具远程访问 四、 Mysql的一些杂项配置(了解)五、 Mysql 逻辑架构…

[UVM]3.核心基类 uvm_object 域的自动化 copy() compare() print() pack unpack

1.核心基类&#xff1a;uvm_object &#xff08;1&#xff09;虚类只能声明&#xff0c;不能例化。 &#xff08;2&#xff09;uvm_object提供的方法 2.域的自动化&#xff08;field automation&#xff09; &#xff08;1&#xff09;简述 &#xff08;2&#xff09;示例 格…

JVM5-垃圾回收

自动垃圾回收 在C/C这类没有自动垃圾回收机制的语言中&#xff0c;一个对象如果不再使用&#xff0c;需要手动释放&#xff0c;否则就会出现内存泄漏&#xff0c;称这种释放对象的过程为垃圾回收&#xff0c;而需要程序员编写代码进行回收的方式为手动回收 内存泄漏指的是不再…

进一步了解CSS布局——WEB开发系列29

CSS 页面布局技术允许我们拾取网页中的元素&#xff0c;并且控制它们相对正常布局流、周边元素、父容器或者主视口/窗口的位置。 一、正常布局流&#xff08;Normal Flow&#xff09; CSS的布局基础是“正常流”&#xff0c;也就是页面元素在没有特别指定布局方式时的默认排列…

OPenCV结构分析与形状描述符(3)计算一个点集的最小外接矩形的函数boundingRect()的使用

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 计算一个点集的最小右上边界矩形或灰度图像中的非零像素。 该函数计算并返回指定点集或灰度图像中非零像素的最小右上边界矩形。 在OpenCV中&am…

[项目][CMP][项目介绍及知识铺垫]详细讲解

目录 1.这个项目做的是什么&#xff1f;2.此项目涉及知识面3.什么是内存池&#xff1f;1.池化技术2.内存池3.内存池主要解决的问题 4.理解malloc 1.这个项目做的是什么&#xff1f; 实现一个高并发内存池&#xff0c;参考原型为Google的一个开源项目tcmalloc(Thread-Caching M…

61、Python之函数高级:为函数添加方法,实现属性可变的装饰器

引言 今天文章的标题&#xff0c;初读起来可能有些拗口&#xff0c;什么叫“为函数添加方法”&#xff1f;但是&#xff0c;如果真正对“Python函数也是对象”这个理念有清晰的理解的话&#xff0c;其实&#xff0c;也是不难理解的&#xff0c;本质上就是给一个对象新增一个自…

通用代码生成器还可以这么用,将MariaDB数据库连数据迁徙到PostgreSQL

通用代码生成器是一种非常方便的软件开发工具&#xff0c;除了简单直接的生成代码&#xff0c;制作快速原型以外。通用代码生成器还可以应用在各种场景上。比如可以使用通用代码生成器&#xff0c;将MariaDB数据库连数据迁徙到PostgreSQL。操作并不复杂&#xff0c;却十分适用。…

【学习笔记】SSL证书之混合加密(Hybrid Encryption)与签名(Signatures)

1、非对称密钥对可以用来进行加密&#xff08;Confidentiality保密性&#xff09; 举个栗子&#xff0c;现在有2个人&#xff0c;Pam和Jim&#xff0c;两人之间需要通过非对称密钥对来给另一方发送数据。Pam通过某种途径将公钥分享给Jim&#xff0c;两人都各自保存着自己的私钥…

算法打卡 Day29(回溯算法)-复原 IP 地址 + 子集 + 子集 Ⅱ

文章目录 Leetcode 93-复原 IP 地址题目描述解题思路 Leetcode 78-子集题目描述解题思路 Leetcode 90-子集 Ⅱ题目描述解题思路 Leetcode 93-复原 IP 地址 题目描述 https://leetcode.cn/problems/restore-ip-addresses/description/ 解题思路 这是一道切割问题&#xff0c;…

【ShuQiHere】“初识人工智能:智能机器的基础入门”

1.1. 引言 (Introduction) &#x1f9e0;&#x1f916; 人工智能&#xff08;Artificial Intelligence, AI&#xff09; 是计算机科学的一个分支&#xff0c;目标是让计算机或机器具备像人类一样的智能。自计算机发明以来&#xff0c;计算机执行各种任务的能力呈指数增长。随着…