vue3-H5实现折叠板组件封装

565 阅读3分钟

前言

实现效果: 折叠版,默认收起内容,点击展开。
实现思路: 使用canvas绘画横线和圆形以及箭头图标,通过slot插槽放置收起内容。

背景: 使用框架vue3,使用组件库Vant@3.6.5

实现效果:

image.png

代码

template

<template>
  <div class="encCollapseStyle">
    <canvas id="myCanvas2" :width="clientWidth" :height="clientHeight" style="display: none"
      >您的浏览器不支持 HTML5 canvas 标签。
    </canvas>

    <canvas id="myCanvas1" width="40" height="20" style="display: none"
      >您的浏览器不支持 HTML5 canvas 标签。
    </canvas>
    <div v-if="!isOpen">
      <div @click="clickOpen(true)" style="text-align: center; height: 0.6rem; font-size: 0.3rem">
        <img :src="blueCircle" />
        <span class="openCollapse">展开</span>
        <img :src="arrow" :width="imgWidth" style="padding-bottom: 0.072rem" />
      </div>
    </div>

    <div v-if="isOpen">
      <!-- 插槽-内容 -->
      <slot></slot>
    </div>
  </div>
</template>

script setup

根据设备适配圆形半径

判断设备类型

// 1.判断设备类型
const judgIsEquipmentType = () => {
  var ua = navigator.userAgent,
    isWindowsPhone = /(?:Windows Phone)/.test(ua),
    isSymbian = /(?:SymbianOS)/.test(ua) || isWindowsPhone,
    isAndroid = /(?:Android)/.test(ua),
    isFireFox = /(?:Firefox)/.test(ua),
    isChrome = /(?:Chrome|CriOS)/.test(ua),
    isTablet =
      /(?:iPad|PlayBook)/.test(ua) ||
      (isAndroid && !/(?:Mobile)/.test(ua)) ||
      (isFireFox && /(?:Tablet)/.test(ua)),
    isPhone = /(?:iPhone)/.test(ua) && !isTablet,
    isPc = !isPhone && !isAndroid && !isSymbian;

  if (isAndroid || isPhone) {
    // 手机
    return 'isAndoidOrPhone';
  } else if (isTablet) {
    // 平板
    return 'isTablet';
  } else if (isPc || isChrome) {
    // 电脑
    return 'isPc';
  }
};

由设备类型确定圆形半径

// 由设备类型确定圆形半径
const judeCircleRadius = () => {
  let res = judgIsEquipmentType();
  if (res === 'isAndoidOrPhone') {
    radius = 2.5;
    imgWidth.value = 11;
    clientHeight.value = 24;
    marginWidth.value = 17;
  }
  if (res === 'isTablet') {
    radius = 5.5;
    imgWidth.value = 22;
    clientHeight.value = 27;
    marginWidth.value = 36;
  }
  if (res === 'isPc') {
    radius = 8.5;
    imgWidth.value = 30;
    clientHeight.value = 30;
    marginWidth.value = 23;
  }
};

canavs绘制图形图片

// 绘制蓝色圆圈○
const convertImageToCanvas = (
  r,
  lineWidth,
  width,
  marginWidth,
  circleColor,
  circleBorderColor = '#0084FF',
  height = 20,
) => {
  // 处理宽度
  let ctxlineWidth = lineWidth / 2 - r;

  // =============canvas=================================
  var c2 = document.getElementById('myCanvas2');
  var ctx2 = c2.getContext('2d');

  // 线
  ctx2.beginPath();
  ctx2.strokeStyle = '#E5E5E5'; // #E5E5E5
  ctx2.moveTo(marginWidth, height + 0.5);
  ctx2.lineTo(ctxlineWidth, height + 0.5);

  ctx2.stroke();

  ctx2.beginPath();
  ctx2.moveTo(ctxlineWidth + r * 2, height + 0.5);
  ctx2.lineTo(width - marginWidth, height + 0.5);
  ctx2.stroke();

  // 圆
  ctx2.beginPath();
  ctx2.arc(lineWidth / 2, height, r + 0.5, 0, 2 * Math.PI);
  // ctx2.lineWidth = 1;
  ctx2.strokeStyle = circleBorderColor;
  ctx2.stroke();
  // 转化为图片 - 分割线
  var image1 = new Image();
  image1.width = width;
  image1.src = c2.toDataURL('image/png');

  // 绘制箭头
  var c1 = document.getElementById('myCanvas1');
  var ctx1 = c1.getContext('2d');
  ctx1.beginPath();
  ctx1.lineWidth = 4;
  ctx1.moveTo(0, 0);
  ctx1.lineTo(20, 20);
  ctx1.lineTo(40, 0);
  ctx1.strokeStyle = '#737373';
  ctx1.stroke();
  // 转化为图片-箭头
  var image2 = new Image();
  image2.width = 40;
  image2.src = c1.toDataURL('image/png');

  return [image1.src, image2.src];
};

整体代码

<template>
  <div class="encCollapseStyle">
    <canvas id="myCanvas2" :width="clientWidth" :height="clientHeight" style="display: none"
      >您的浏览器不支持 HTML5 canvas 标签。
    </canvas>

    <canvas id="myCanvas1" width="40" height="20" style="display: none"
      >您的浏览器不支持 HTML5 canvas 标签。
    </canvas>
    <div v-if="!isOpen">
      <div @click="clickOpen(true)" style="text-align: center; height: 0.6rem; font-size: 0.3rem">
        <img :src="blueCircle" />
        <span class="openCollapse">展开</span>
        <img :src="arrow" :width="imgWidth" style="padding-bottom: 0.072rem" />
      </div>
    </div>

    <div v-if="isOpen">
      <!-- 插槽-内容 -->
      <slot></slot>
    </div>
  </div>
</template>

<script setup>
import { onMounted, ref } from 'vue';
// 设备宽度
let clientWidth = ref(window.innerWidth);
let clientHeight = ref(100);
let marginWidth = ref(17);

let radius = 10; // 圆圈半径-适配
let imgWidth = ref(''); // 箭头适配宽度
// 蓝色圆圈+横线
const blueCircle = ref();
// 展开的箭头
const arrow = ref();

// 是否显示内容-展开
let isOpen = ref(false);

/**
 * 适配圆形半径
 */
// 1.判断设备类型
const judgIsEquipmentType = () => {
  var ua = navigator.userAgent,
    isWindowsPhone = /(?:Windows Phone)/.test(ua),
    isSymbian = /(?:SymbianOS)/.test(ua) || isWindowsPhone,
    isAndroid = /(?:Android)/.test(ua),
    isFireFox = /(?:Firefox)/.test(ua),
    isChrome = /(?:Chrome|CriOS)/.test(ua),
    isTablet =
      /(?:iPad|PlayBook)/.test(ua) ||
      (isAndroid && !/(?:Mobile)/.test(ua)) ||
      (isFireFox && /(?:Tablet)/.test(ua)),
    isPhone = /(?:iPhone)/.test(ua) && !isTablet,
    isPc = !isPhone && !isAndroid && !isSymbian;

  if (isAndroid || isPhone) {
    // 手机
    return 'isAndoidOrPhone';
  } else if (isTablet) {
    // 平板
    return 'isTablet';
  } else if (isPc || isChrome) {
    // 电脑
    return 'isPc';
  }
};
// 由设备类型确定圆形半径
const judeCircleRadius = () => {
  let res = judgIsEquipmentType();
  if (res === 'isAndoidOrPhone') {
    radius = 2.5;
    imgWidth.value = 11;
    clientHeight.value = 24;
    marginWidth.value = 17;
  }
  if (res === 'isTablet') {
    radius = 5.5;
    imgWidth.value = 22;
    clientHeight.value = 27;
    marginWidth.value = 36;
  }
  if (res === 'isPc') {
    radius = 8.5;
    imgWidth.value = 30;
    clientHeight.value = 30;
    marginWidth.value = 23;
  }
};
judeCircleRadius();

/**
 *  canvas
 */
// 绘制蓝色圆圈○
const convertImageToCanvas = (
  r,
  lineWidth,
  width,
  marginWidth,
  circleColor,
  circleBorderColor = '#0084FF',
  height = 20,
) => {
  // 处理宽度
  let ctxlineWidth = lineWidth / 2 - r;

  // =============canvas=================================
  var c2 = document.getElementById('myCanvas2');
  var ctx2 = c2.getContext('2d');

  // 线
  ctx2.beginPath();
  ctx2.strokeStyle = '#E5E5E5'; // #E5E5E5
  ctx2.moveTo(marginWidth, height + 0.5);
  ctx2.lineTo(ctxlineWidth, height + 0.5);

  ctx2.stroke();

  ctx2.beginPath();
  ctx2.moveTo(ctxlineWidth + r * 2, height + 0.5);
  ctx2.lineTo(width - marginWidth, height + 0.5);
  ctx2.stroke();

  // 圆
  ctx2.beginPath();
  ctx2.arc(lineWidth / 2, height, r + 0.5, 0, 2 * Math.PI);
  // ctx2.lineWidth = 1;
  ctx2.strokeStyle = circleBorderColor;
  ctx2.stroke();
  // 转化为图片 - 分割线
  var image1 = new Image();
  image1.width = width;
  image1.src = c2.toDataURL('image/png');

  // 绘制箭头
  var c1 = document.getElementById('myCanvas1');
  var ctx1 = c1.getContext('2d');
  ctx1.beginPath();
  ctx1.lineWidth = 4;
  ctx1.moveTo(0, 0);
  ctx1.lineTo(20, 20);
  ctx1.lineTo(40, 0);
  ctx1.strokeStyle = '#737373';
  ctx1.stroke();
  // 转化为图片-箭头
  var image2 = new Image();
  image2.width = 40;
  image2.src = c1.toDataURL('image/png');

  return [image1.src, image2.src];
};

// 点击展开/收回
const clickOpen = () => {
  isOpen.value = isOpen;
};

onMounted(() => {
  let result = convertImageToCanvas(
    radius,
    clientWidth.value,
    clientWidth.value,
    marginWidth.value,
  );
  blueCircle.value = result[0];
  arrow.value = result[1];
});

/**
 * README
 * 组件描述:仅支持点击展开,初始页面未展开内容;
 * 参数:暂无参数
 */
</script>

<style>
.encCollapseStyle {
}
.openCollapse {
  padding-right: 0.1rem;
  color: #737373;
  font-size: 0.4rem;
}
#container {
  zoom: 0.5;
}
</style>

使用

<EncCollapse style="background: #ffffff; padding-bottom: 1.1rem">
  // 需折叠内容
</EncCollapse>

若有问题,请多多指教。🎃