Skip to content

自定义 Element-Plus 主题颜色

bash
$ npm install element-plus --save		# 安装 element-plus 组件库
$ npm install -D unplugin-vue-components unplugin-auto-import		# 安装自动导入插件
$ npm install -D sass					# 安装 sass 预处理器工具

第一种方式: 全局导入

注意

  • 这种方式只需要在 main.ts 中引入自定义的主题样式文件 styles/element/index.scss 即可。
scss
// src/styles/element/index.scss
/* 只需要重写你需要的即可 */
@forward "element-plus/theme-chalk/src/common/var.scss" with (
    $colors: (
        "white": #ffffff,
        "black": #000000,
        "primary": (
            "base": #0d6efd
        ),
        "success": (
            "base": #20c997
        ),
        "warning": (
            "base": #ffc107
        ),
        "danger": (
            "base": #e03f4f
        ),
        "error": (
            "base": #ce2929
        ),
        "info": (
            "base": #09b2d4
        )
    )
);

// 导入element-plus index.scss源文件来进行主题合并编译
@use "element-plus/theme-chalk/src/index.scss" as *;
ts
import './styles/element/index.scss'
import ElementPlus from 'element-plus'

const app = createApp(App)

app.use(ElementPlus)
vue
<script setup lang='ts'>

</script>

<template>
  <div>
    <el-button type="info">Click</el-button>
    <el-button type="primary">Click</el-button>
    <el-button type="success">Click</el-button>
    <el-button type="danger">Click</el-button>
    <el-button type="warning">Click</el-button>
  </div>
</template>

<style scoped lang='scss'></style>

第二种方式: 按需导入

注意

  • 这种方式只需要配置 vite.config.ts 文件, main.ts 中不再需要进行任何引入。亲测有效!
scss
// src/styles/element/index.scss
/* 只需要重写你需要的即可 */
@forward "element-plus/theme-chalk/src/common/var.scss" with (
    $colors: (
        "white": #ffffff,
        "black": #000000,
        "primary": (
            "base": #0d6efd
        ),
        "success": (
            "base": #20c997
        ),
        "warning": (
            "base": #ffc107
        ),
        "danger": (
            "base": #e03f4f
        ),
        "error": (
            "base": #ce2929
        ),
        "info": (
            "base": #09b2d4
        )
    )
);
ts
import { fileURLToPath, URL } from 'node:url'

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'

// 自动导入
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [
    vue(),
    // use unplugin-auto-import
    AutoImport({
      resolvers: [ElementPlusResolver()],
    }),
    // // use unplugin-vue-components
    Components({
      resolvers: [
        ElementPlusResolver({
          importStyle: "sass",
        }),
      ],
    }),
  ],
  resolve: {
    alias: {
      '@': fileURLToPath(new URL('./src', import.meta.url))
    },
  },
  // use vite-plugin-windicss
  css: {
    preprocessorOptions: {
      scss: {
        additionalData: `@use "@/styles/element/index.scss" as *;`,
      },
    },
  }
})
vue
<script setup lang='ts'>

</script>

<template>
  <div>
    <el-button type="info">Click</el-button>
    <el-button type="primary">Click</el-button>
    <el-button type="success">Click</el-button>
    <el-button type="danger">Click</el-button>
    <el-button type="warning">Click</el-button>
  </div>
</template>

颜色渐变

linear-gradient(to bottom, #27ae60, #e74c3c)
源代码
vue
<script setup lang='ts'>
import { onMounted, ref, watch } from 'vue';
import { Direction, Position, type DirectionOption } from '../types';

// 渐变方向
const direction = ref<Direction>(Direction.bottom);

// 遍历 Direction, 将其转换为 options
const options = ref<DirectionOption[]>([
    {
        label: Position.top,
        value: Direction.top
    },
    {
        label: Position.bottom,
        value: Direction.bottom
    },
    {
        label: Position.left,
        value: Direction.left
    },
    {
        label: Position.right,
        value: Direction.right
    },
    {
        label: Position.leftBottom,
        value: Direction.leftBottom
    },
    {
        label: Position.rightTop,
        value: Direction.rightTop
    },
    {
        label: Position.leftTop,
        value: Direction.leftTop
    },
    {
        label: Position.rightBottom,
        value: Direction.rightBottom
    }
]);

// related to the div element
const div = ref<HTMLDivElement | null>(null);

// 开始颜色
const startColor = ref<string>('#27ae60');
// 结束颜色
const endColor = ref<string>('#e74c3c');

watch(direction, () => {
    div.value!.style.background = `linear-gradient(to ${direction.value}, ${startColor.value}, ${endColor.value})`;
});

onMounted(() => {
    div.value!.style.width = '400px';
    div.value!.style.height = '200px';
    div.value!.style.background = `linear-gradient(to ${direction.value}, ${startColor.value}, ${endColor.value})`;
})

</script>

<template>
    <div style="display: flex; justify-content:space-between;">
        <div ref="div" style="text-align: center; line-height: 100px;">
            {{ `linear-gradient(to ${direction}, ${startColor}, ${endColor})` }}
        </div>
        <el-select v-model="direction" placeholder="渐变方向" size="large" style="width: 240px; margin-right: 10px;">
            <el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value" />
        </el-select>
    </div>
</template>

<style scoped lang='scss'></style>

3D 骰子

1
2
3
4
5
6
源代码
vue
<script setup lang="ts"></script>

<template>
  <div class="dice-box">
    <div class="dice">
      <div class="face">1</div>
      <div class="face">2</div>
      <div class="face">3</div>
      <div class="face">4</div>
      <div class="face">5</div>
      <div class="face">6</div>
    </div>
  </div>
</template>

<style scoped lang="scss">
@use 'sass:math';
@use 'sass:list';
@use 'sass:map';

.dice-box {
  width: 200px;
  height: 200px;
  margin: 80px auto;
}

@mixin d-flex {
  display: flex;
  justify-content: center;
  align-items: center;
}

$colors: (
  1: #3498db,
  2: #e74c3c,
  3: #f1c40f,
  4: #9b59b6,
  5: #34495e,
  6: #e67e22,
);

$size: (200px, 200px);

$font-size: 60px;

// 生成随机角度
@function randomAngle() {
  @return math.random($limit: 360) + deg;
}

// 骰子随机旋转
@keyframes randomRotate {
  25% {
    transform: rotateX(randomAngle()) rotateY(randomAngle()) rotateZ(randomAngle());
  }

  50% {
    transform: rotateX(randomAngle()) rotateY(randomAngle()) rotateZ(randomAngle());
  }

  75% {
    transform: rotateX(randomAngle()) rotateY(randomAngle()) rotateZ(randomAngle());
  }
}

.dice {
  @include d-flex;
  position: relative;
  width: list.nth($size, 1);
  height: list.nth($size, 2);
  transform-style: preserve-3d;
  transform: perspective(900px) rotateX(-30deg);
  animation: randomRotate 1s forwards infinite;

  @for $i from 1 through 6 {
    .face:nth-child(#{$i}) {
      position: absolute;
      width: inherit;
      height: inherit;
      background: map.get($colors, $i);
      line-height: list.nth($list: $size, $n: 1);
      font-size: $font-size;
      color: #fff;
      text-align: center;
    }
  }

  // 对每个面进行旋转定位成骰子立方体的每个面
  // 将每个面旋转到立方体的每个面
  // 利用for循环快速生成每个面
  @for $i from 1 through 6 {
    .face:nth-child(#{$i}) {
      @if $i ==1 {
        transform: rotateX(90deg) translateZ(calc(list.nth($size, 1) / 2));
      }

      @if $i ==2 {
        transform: rotateY(90deg) translateZ(calc(list.nth($size, 1) / 2));
      }

      @if $i ==3 {
        transform: rotateY(-90deg) translateZ(calc(list.nth($size, 1) / 2));
      }

      @if $i ==4 {
        transform: rotateX(-90deg) translateZ(calc(list.nth($size, 1) / 2));
      }

      @if $i ==5 {
        transform: rotateY(180deg) translateZ(calc(list.nth($size, 1) / 2));
      }

      @if $i ==6 {
        transform: rotateX(0deg) translateZ(calc(list.nth($size, 1) / 2));
      }
    }
  }
}
</style>

3D开门

源代码
vue
<template>
    <input type="checkbox" class="space" data-before="开启&nbsp;" data-after="&nbsp;探索" />
</template>

<style scoped lang='scss'>
// 太空
.space {
    $width: 100%;
    $height: 300px;
    display: block;
    // 清除默认外观效果
    appearance: none;

    width: $width;
    height: $height;
    background: url("https://pic3.zhimg.com/v2-1c2ecf961f50e792dad16c3709e9652a_r.jpg") no-repeat;
    background-size: cover;
    overflow: hidden;
    transform-style: preserve-3d;
    transform: perspective(1000px);

    // 做一扇门
    &::before,
    &::after {
        content: '';
        line-height: $height;
        display: block;
        color: #fff;
        letter-spacing: 0.5rem;
        font-size: 3rem;
        width: calc($width / 2);
        height: $height;
        background: #34495e;
        transition: 3s;
        text-shadow: 1px 1px 10px blueviolet;
    }

    &::before {
        content: attr(data-before);
        text-align: right;
        position: absolute;
        top: 0;
        left: 0;
        border-right: 3px solid #2c3e50;
        transform-origin: left bottom;
    }

    &::after {
        content: attr(data-after);
        position: absolute;
        top: 0;
        left: 50%;
        border-left: 3px solid #2c3e50;
        transform-origin: right bottom;
    }

    &:checked {
        &::before {
            text-shadow: -5px -5px 10px rgb(218, 10, 124);
            opacity: 0.95;
            transform: rotateY(90deg) skewY(5deg);
            border-color: transparent;
        }

        &::after {
            text-shadow: -5px -5px 10px rgb(218, 10, 124);
            opacity: 0.95;
            transform: rotateY(90deg) skewY(-5deg);
            border-color: transparent;
        }
    }
}
</style>

网格布局

行间距

1rem

列间距

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
源代码
vue
<template>
  <div class="controller">
    <h3>行间距</h3>
    <input type="range" min="0" max="10" step="0.05" v-model="rowSpan" />
    <p>{{ rowSpan }}rem</p>
    <h3>列间距</h3>
    <input type="range" min="0" max="10" step="0.05" v-model="columnSpan" />
    <p class="mt-3">
      <el-button @click="mergeAandB">合并列</el-button>
    </p>
    <p class="mt-3">
      <el-button @click="mergeCandD">合并行</el-button>
    </p>
    <p class="mt-3">
      <el-button @click="mergeFandG">合并行和列</el-button>
    </p>
  </div>
  <div class="grid-layout" ref="gridContainer">
    <div class="item" ref="item1">1</div>
    <div class="item" ref="item2">2</div>
    <div class="item">3</div>
    <div class="item" ref="item4">4</div>
    <div class="item">5</div>
    <div class="item" ref="item6">6</div>
    <div class="item" ref="item7">7</div>
    <div class="item" ref="item8">8</div>
    <div class="item">9</div>
    <div class="item" ref="item10">10</div>
    <div class="item" ref="item11">11</div>
    <div class="item">12</div>
    <div class="item">13</div>
    <div class="item">14</div>
    <div class="item">15</div>
    <div class="item">16</div>
  </div>
</template>

<script setup lang="ts">
import { ref, watch } from 'vue'

const gridContainer = ref<HTMLDivElement | null>(null)
// 合并 1 和 2 网格元素: 合并行
const item1 = ref<HTMLDivElement | null>(null)
const item2 = ref<HTMLDivElement | null>(null)

// 合并 4 和 8 网格元素: 合并列
const item4 = ref<HTMLDivElement | null>(null)
const item8 = ref<HTMLDivElement | null>(null)

// 合并 6 和 7, 10 和 11 网格元素: 合并行和列
const item6 = ref<HTMLDivElement | null>(null)
const item7 = ref<HTMLDivElement | null>(null)
const item10 = ref<HTMLDivElement | null>(null)
const item11 = ref<HTMLDivElement | null>(null)

const rowSpan = ref(1)
const columnSpan = ref(1)

watch([rowSpan, columnSpan], ([row, column]) => {
  // 设置行间距
  gridContainer.value?.style.setProperty('grid-row-gap', `${row}rem`)
  // 设置列间距
  gridContainer.value?.style.setProperty('grid-column-gap', `${column}rem`)
})

// 合并 1 和 2 网格元素: 合并行方法
const mergeAandB = () => {
  item2.value?.style.setProperty('display', 'none')
  item1.value?.style.setProperty('grid-column', 'span 2')
}
// 合并 4 和 8 网格元素: 合并列方法
const mergeCandD = () => {
  item8.value?.style.setProperty('display', 'none')
  item4.value?.style.setProperty('grid-row', 'span 2')
}

// 合并 6 和 7, 10 和 11 网格元素: 合并行和列方法
const mergeFandG = () => {
  item7.value?.style.setProperty('display', 'none')
  item10.value?.style.setProperty('display', 'none')
  item11.value?.style.setProperty('display', 'none')
  // 或者 span 行数  / span 列数
  item6.value?.style.setProperty('grid-area', 'span 2 / span 2')
}
</script>

<style scoped lang="scss">
@use 'sass:list';
@use 'sass:math';

.controller {
  width: 200px;
  height: 100%;
  margin-top: 1rem;
}

.grid-layout {
  width: 600px;
  height: 500px;
  display: grid;
  grid-template-rows: repeat(4, 1fr); // 父盒子分成多少行, 每行的高度是1fr, 1fr表示剩余空间的比例, 1fr表示平均分配多少份
  grid-template-columns: repeat(4, 1fr);
  // gap: 1rem 1.5rem; // 行、列间距的简写, 可以简写一个值
  grid-row-gap: 1rem; // 行间距
  grid-column-gap: 1rem; // 列间距
  transition: 0.5s;

  > .item {
    display: flex;
    justify-content: center;
    align-items: center;
    background: #e67e22;
    color: #fff;
    font-size: 1rem;
    border-radius: 5px;
    transition: all 0.5s;
  }
}

@for $i from 0 to 16 {
  .item:nth-child(#{$i + 1}) {
    // background: hsl(calc($i * 40%), 100%, 74%);
    /** 将上面的计算换一种方式, 因为 sass 不支持 calc 运算符 */
    background: hsl(calc(#{$i} * 40%), 100%, 74%);
  }

  .grid-layout:has(.item:nth-child(#{$i + 1}):hover) {
    $r: math.floor(calc($i / 4 + 1));
    $c: $i % 4 + 1;
    $arr: 1fr 1fr 1fr 1fr;
    $rows: list.set-nth($arr, $r, 2fr);
    $columns: list.set-nth($arr, $c, 2fr);
    grid-template-rows: $rows;
    grid-template-columns: $columns;
  }
}
</style>

星空

Sass 绘制的星空
源代码
vue
<template>
  <div class="starsky-container">
    <div class="layer1"></div>
    <div class="layer2"></div>
    <div class="layer3"></div>
    <div class="title">Sass 绘制的星空</div>
  </div>
</template>

<style scoped lang="scss">
@use 'sass:math';
@use 'sass:list';
@use 'sass:string';

/* 获取随机颜色 */
@function randomColor() {
  $r: math.random(255);
  $g: math.random(255);
  $b: math.random(255);
  @return rgb($r, $g, $b);
}

/* 盒子阴影设置 */
@function getShadows(
  $n: 500,
  $bound-sizes: (
    400px,
    300px,
  )
) {
  $bound-width: list.nth(
    $list: $bound-sizes,
    $n: 1,
  );
  $bound-height: list.nth(
    $list: $bound-sizes,
    $n: 2,
  );
  $shadows: '#{math.random($bound-width)}px #{math.random($bound-height)}px #{randomColor()}';

  @for $i from 2 through $n {
    $shadows: '#{$shadows},#{math.random($bound-width)}px #{math.random($bound-height)}px #{randomColor()}';
  }

  @return string.unquote($shadows);
}

.starsky-container {
  /** 边界宽高 */
  $sizes: (700, 300) !default;
  /*动画时长*/
  $duration: 100s;
  /* 亮点数量 */
  $count: 1500;

  position: relative;
  // Dart 3.0.0 不再支持
  width: #{list.nth($list: $sizes, $n: 1)}px;
  height: #{list.nth($list: $sizes, $n: 2)}px;
  overflow: hidden;

  /* 标题*/
  .title {
    // 当鼠标移动到元素上时 显示指针的形状
    touch-action: none;
    user-select: none;
    cursor: default;
    position: absolute;
    top: 50%;
    left: 0;
    right: 0;
    color: #fff;
    text-align: center;
    font-family: 'Arial', 'sans-serif';
    font-size: 26px;
    letter-spacing: 10px;
    margin-top: -60px;
    padding-left: 10px;
    line-height: #{calc(list.nth($sizes, 2) / 2)}px;
    background: linear-gradient(to top, white, #38495a);
    background-clip: text;
    -webkit-background-clip: text;
    -webkit-background-clip: text;
    color: transparent;
  }

  @for $i from 1 through 3 {
    $duration: math.floor(math.div($duration, 2));
    $count: math.floor(math.div($count, 2));

    .layer#{$i} {
      $size: #{$i}px;
      // position: fixed;
      position: relative;
      width: $size;
      height: $size;
      border-radius: 50%;
      background: #f40;
      left: 0;
      top: 0;
      box-shadow: getShadows($count, $sizes);
      animation: moveUp $duration linear infinite;

      &::after {
        content: '';
        position: fixed;
        left: 0;
        top: 300px;
        width: $size;
        height: $size;
        border-radius: inherit;
        box-shadow: inherit;
      }
    }
  }

  @keyframes moveUp {
    100% {
      transform: translateY(-#{list.nth($list: $sizes, $n: 2)}px);
    }
  }
}
</style>

Sass星空源代码

详情:
html
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        html,
        body {
            width: 100vw;
            height: 100vh;
            /* background-color: #334455; */
            position: relative;
        }

        .pointer,
        .pointer::after,
        .pointer::after::after {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        .pointer {
            width: 100px;
            height: 100px;
            border: 1px solid rgb(239, 239, 239);
            border-radius: 50%;
            position: absolute;
            animation: 0.8s ease-in-out 1 scale;
            background-color: rgba(255, 255, 255, 0.7);
            transition: all 0.4s;
        }

        .pointer::after {
            content: '';
            display: block;
            width: 100px;
            height: 100px;
            border: 1px solid rgb(239, 239, 239);
            border-radius: 50%;
            position: absolute;
            animation: 0.8s ease-in 1 alternate scale;
            transition: all 0.8s;
            background-color: rgba(255, 255, 255, 0.8);
        }

        .pointer::after::after {
            content: '';
            display: block;
            width: 100px;
            height: 100px;
            border: 1px solid rgb(239, 239, 239);
            border-radius: 50%;
            position: absolute;
            animation: 0.7s ease-out 0.1s 1 alternate scale;
            transition: all 0.8s;
            background-color: rgba(255, 255, 255, 0.9);
        }

        @keyframes scale {
            0% {
                opacity: 0;
                transform: scale(0);
                border-color: rgba(255, 255, 255, 0.2);
            }

            100% {
                opacity: 0.5;
                transform: scale(1);
                border-color: rgba(255, 255, 255, 0.1);
                background-color: transparent;
            }
        }
    </style>
</head>

<body>
    <div class="layer1"></div>
    <div class="layer2"></div>
    <div class="layer3"></div>
    <div class="title">Sass 星空</div>

    <script>
        window.addEventListener("click", (e) => {
            const pointer = document.createElement("div");
            pointer.classList.add("pointer");
            /**获取元素的宽高, 计算点击的是元素的中心位置 */
            // 获取元素宽高
            const { width, height } = pointer.getBoundingClientRect();
            pointer.style.left = `${e.clientX - 50}px`;
            pointer.style.top = `${e.clientY - 50}px`;
            document.body.appendChild(pointer);
            pointer.addEventListener("animationend", () => {
                pointer.remove();
            });
        });
    </script>
</body>

</html>
scss
html {
  height: 100%;
  background: radial-gradient(ellipse at bottom, #1b2735 0%, #090a0f 100%);
  overflow: hidden;
}
/* 标题*/
.title {
  // 当鼠标移动到元素上时 显示指针的形状
  touch-action: none;
  user-select: none;
  cursor: default;
  position: absolute;
  top: 50%;
  left: 0;
  right: 0;
  color: #fff;
  text-align: center;
  font-family: "Arial", "sans-serif";
  font-size: 50px;
  letter-spacing: 10px;
  margin-top: -60px;
  padding-left: 10px;
  background: linear-gradient(white, #38495a);
  background-clip: text;
  -webkit-background-clip: text;
  -webkit-background-clip: text;
  color: transparent;
}
/* 盒子阴影设置 */
@function getShadows($n) {
  $shadows: "#{random(100)}vw #{random(100)}vh #{randomColor()}";
  @for $i from 2 through $n {
    $shadows: "#{$shadows},#{random(100)}vw #{random(100)}vh #{randomColor()}";
  }
  @return unquote($shadows);
}
/* 获取随机颜色 */
@function randomColor() {
  $r: random(255);
  $g: random(255);
  $b: random(255);
  @return rgb($r, $g, $b);
}
/*动画时长*/
$duration: 100s;
/* 亮点数量 */
$count: 1500;

@for $i from 1 through 3 {
  $duration: floor($duration / 2);
  $count: floor($count / 2);
  .layer#{$i} {
    $size: #{$i}px;
    position: fixed;
    width: $size;
    height: $size;
    border-radius: 50%;
    background: #f40;
    left: 0;
    top: 0;
    box-shadow: getShadows($count);
    animation: moveUp $duration linear infinite;
    &::after {
      content: "";
      position: fixed;
      left: 0;
      top: 100vh;
      width: $size;
      height: $size;
      border-radius: inherit;
      box-shadow: inherit;
    }
  }
}

@keyframes moveUp {
  100% {
    transform: translateY(-100vh);
  }
}

爱心动画

源代码
vue
<script setup lang='ts'>
import { ref } from 'vue'
</script>

<template>
    <div class="heart heartbeat" style="margin: 0 auto;"></div>
</template>

<style scoped lang='scss'>
@keyframes heartbeat {
    from {
        opacity: 0.2;
    }

    25% {
        opacity: 1;
        transform: rotate(45deg) scale(0.35);
    }

    50% {
        transform: rotate(45deg) scale(0.5);
    }

    75% {
        opacity: 1;
        transform: rotate(45deg) scale(0.35);
    }

    to {
        transform: rotate(45deg) scale(0.5);
    }
}

.heart {
    width: 200px;
    height: 200px;
    background-color: #e74c3c;
    position: relative;
    transform: rotate(45deg) scale(0.5);

    &.heartbeat {
        animation-name: heartbeat;
        animation-duration: 1s;
        animation-iteration-count: infinite;
        animation-timing-function: ease-in-out;
        animation-direction: reverse;
    }

    &::before,
    &::after {
        content: '';
        width: 200px;
        height: 200px;
        border-radius: 100%;
        position: absolute;
    }

    &::before {
        background-color: inherit;
        transform: translateY(-50%);
    }

    &::after {
        background-color: inherit;
        transform: translateX(-50%);
    }
}
</style>

文本下划线

Hello world!

源代码
vue
<template>
    <h1 class="demo">Hello world!</h1>
</template>

<style scoped lang='scss'>
@mixin textDecoration( // solid, double, dotted, dashed, wavy

    $textLine: wavy,
    // none, underline, overline, line-throughd, blink
    $textStyle: underline,
    // 颜色
    $textColor: red,
    // 文本下划线的偏移量
    $textOffset: 0.5em,
    // 文本渐变颜色
    $backgroundImage: linear-gradient(to right, red, green, blue)) {
    // 设置下划线的样式
    text-decoration: $textLine $textStyle $textColor;
    // 设置下划线的偏移量
    text-underline-offset: $textOffset;
    // 设置下划线渐变颜色
    background-image: transparent;
}

/** 使用伪元素设置文本下划线 */
@mixin textDecorationPseudo {
    color: linear-gradient(to right, red, blue);
    width: max-content;

    &::after {
        content: "";
        display: block;
        width: 100%;
        height: 1px;
        background: linear-gradient(to right, red, green, blue);
    }
}


.demo {
    @include textDecoration;
}
</style>

太极图

源代码
vue
<script setup lang='ts'>
import { ref } from 'vue'
</script>

<template>
    <div class="taiji"></div>
</template>

<style scoped lang='scss'>
.taiji {
    --taiji-width: 300px;
    --taiji-height: 300px;
    resize: both;
    width: var(--taiji-width);
    height: var(--taiji-height);
    background: #eee;
    /** 这两项配合可以实现用户自己放大缩小盒子 */
    resize: both;
    overflow: auto;

}
</style>

平滑滚动

哪个容器盒子要实现平滑滚动,就给哪个盒子添加该属性

scss
html {
    scroll-behavior: smooth;
}

3D 立方体

源代码
vue
<script setup lang='ts'>
import { ref } from 'vue'
</script>

<template>
    <div class="cube" style="--cube-width: 300px; --cube-height: 300px;">
        <div style="--position: 1"></div>
        <div style="--position: 2"></div>
        <div style="--position: 3"></div>
        <div style="--position: 4"></div>
        <div style="--position: 5"></div>
        <div style="--position: 6"></div>
    </div>
</template>

<style scoped lang='scss'>
@property --last-counter {
    syntax: "<integer>";
    inherits: false;
    initial-value: 4;
}

@property --angle {
    syntax: "<angle>";
    inherits: false;
    initial-value: calc(360deg / 4);
}

@property --transform-origin {
    syntax: "<angle>";
    inherits: true;
    initial-value: 90deg;
}

@keyframes CubeAutoPlay {
    to {
        transform: perspective(1000px) rotateX(-30deg) rotateY(360deg) rotateZ(360deg);
    }
}

.cube {
    position: relative;
    width: var(--cube-width, 300px);
    height: var(--cube-height, 300px);
    /* background: #34495e; */
    /* border: 1px solid black; */
    margin: 200px auto;
    transform-style: preserve-3d;
    transform: perspective(1000px) rotateX(calc(var(--transform-origin)));
    animation: CubeAutoPlay 3s linear infinite alternate;
    /* -webkit-box-reflect: below 0px linear-gradient(transparent, rgba(0, 0, 0, 0.2)); */

    >div {
        position: absolute;
        top: 0;
        left: 0;
        width: inherit;
        height: inherit;
        background-size: cover;
        background-repeat: no-repeat;


        &:nth-child(1) {
            background-color: #1abc9c;
            background-image: url('https://picsum.photos/300/300?random=1');
            transform: rotateY(calc(var(--angle) * calc(var(--position) - 1))) translateZ(calc(var(--cube-width) / 2));

        }

        &:nth-child(2) {
            background-color: #2ecc71;
            background-image: url('https://picsum.photos/300/300?random=2');
            transform: rotateY(calc(var(--angle) * calc(var(--position) - 1))) translateZ(calc(var(--cube-width) / 2));
        }

        &:nth-child(3) {
            background-color: #3498db;
            background-image: url('https://picsum.photos/300/300?random=3');
            transform: rotateY(calc(var(--angle) * calc(var(--position) - 1))) translateZ(calc(var(--cube-width) / 2));
        }

        &:nth-child(4) {
            background-color: #e67e22;
            background-image: url('https://picsum.photos/300/300?random=4');
            transform: rotateY(calc(var(--angle) * calc(var(--position) - 1))) translateZ(calc(var(--cube-width) / 2));
        }

        &:nth-child(5) {
            background-color: #e74c3c;
            background-image: url('https://picsum.photos/300/300?random=5');
            transform: rotateX(calc(-1 * var(--transform-origin))) translateZ(calc(-1 * var(--cube-width) / 2));
        }

        &:nth-child(6) {
            background-color: #f39c12;
            background-image: url('https://picsum.photos/300/300?random=6');
            transform: translateY(calc(var(--cube-height) / 2)) rotateX(calc(var(--transform-origin)));
        }
    }
}
</style>

3D 相册

css
@property --last-counter {
    /** 规定这个值的类型, 当允许外部修改时, 如果修改的值类型不属于规定的数据类型, 将不会生效, "*" 表示允许所有的值 */
    syntax: "<integer>";
    /** 是否允许外部修改该属性 */
    inherits: false;
    /** 初始值 */
    initial-value: 4;
}
  • syntax: 规定这个值的类型, 当允许外部修改时, 如果修改的值类型不属于规定的语法的数据类型, 将不会生效, 继续使用初始值, "*" 表示允许所有的值;
  • inherits: 是否允许外部修改该属性, 即重新给该变量赋值。
  • initial-value: 给定默认的初始值。
源代码
vue
<script setup lang='ts'>
import { ref } from 'vue'
</script>

<template>
    <div class="cube" style="--cube-width: 200px; --cube-height: 200px;">
        <div style="--position: 1"></div>
        <div style="--position: 2"></div>
        <div style="--position: 3"></div>
        <div style="--position: 4"></div>
    </div>
</template>

<style scoped lang='scss'>
@property --last-counter {
    /** 规定这个值的类型, 当允许外部修改时, 如果修改的值类型不属于规定的数据类型, 将不会生效, "*" 表示允许所有的值 */
    syntax: "<integer>";
    /** 是否允许外部修改该属性 */
    inherits: false;
    /** 初始值 */
    initial-value: 4;
}

@property --angle {
    syntax: "<angle>";
    inherits: false;
    initial-value: calc(360deg / 4);
}

@property --photo-transform-originX {
    syntax: "<angle>";
    inherits: true;
    initial-value: -30deg;
}

@property --photo-transform-originY {
    syntax: "<angle>";
    inherits: true;
    initial-value: 0deg;
}

@keyframes CubeAutoPlay {
    to {
        --photo-transform-originY: 360deg;
    }
}

.cube {
    position: relative;
    width: var(--cube-width, 300px);
    height: var(--cube-height, 300px);
    /* background: #34495e; */
    /* border: 1px solid black; */
    margin: 200px auto;
    transform-style: preserve-3d;
    transform: perspective(1000px) rotateX(var(--photo-transform-originX)) rotateY(var(--photo-transform-originY));
    animation: CubeAutoPlay 3s linear infinite alternate;

    &:hover {
        animation-play-state: paused;
    }

    >div {
        position: absolute;
        top: 0;
        left: 0;
        width: inherit;
        height: inherit;
        background-size: cover;
        background-repeat: no-repeat;
        -webkit-box-reflect: below 0px linear-gradient(transparent, rgba(0, 0, 0, 0.2));


        &:nth-child(1) {
            background-color: #1abc9c;
            background-image: url('https://picsum.photos/300/300?random=1');
            transform: rotateY(calc(var(--angle) * calc(var(--position) - 1))) translateZ(calc(var(--cube-width)));

        }

        &:nth-child(2) {
            background-color: #2ecc71;
            background-image: url('https://picsum.photos/300/300?random=2');
            transform: rotateY(calc(var(--angle) * calc(var(--position) - 1))) translateZ(calc(var(--cube-width)));
        }

        &:nth-child(3) {
            background-color: #3498db;
            background-image: url('https://picsum.photos/300/300?random=3');
            transform: rotateY(calc(var(--angle) * calc(var(--position) - 1))) translateZ(calc(var(--cube-width)));
        }

        &:nth-child(4) {
            background-color: #e67e22;
            background-image: url('https://picsum.photos/300/300?random=4');
            transform: rotateY(calc(var(--angle) * calc(var(--position) - 1))) translateZ(calc(var(--cube-width)));
        }
    }
}
</style>

网格布局

非常简单的实现各种常见网页布局

1
3
4
源代码
vue
<script setup lang='ts'>
</script>

<template>
    <div class="body-demo">
        <header>1</header>
        <aside>2</aside>
        <main>3</main>
        <footer>4</footer>
    </div>
</template>

<style scoped lang='css'>
@property --sidebar-width {
    /** 属性值的数据类型, 后续更改会依据数据类型是否一致从而决定是否覆盖该属性 */
    syntax: "<length>";
    /** 是否允许继承, 允许继承也就表示可以重写, 不允许继承则会始终使用初始值 */
    inherits: true;
    /** 初始值 */
    initial-value: 200px;
}

.body-demo {
    * {
        margin: 0;
        padding: 0;
        box-sizing: content-box;
    }

    min-height: 300px;
    display: grid;
    grid-template-rows: auto 1fr auto;
    grid-template-columns: var(--sidebar-width) 1fr;
    grid-template-areas: "navbar navbar"
    "sidebar main"
    "sidebar footer";
}

header {
    grid-area: navbar;
    background-color: #f1c40f;
    height: 100px;
}

aside {
    grid-area: sidebar;
    background-color: #3498db;
}

main {
    grid-area: main;
    background-color: #2ecc71;
}

footer {
    grid-area: footer;
    background-color: #e74c3c;
}
</style>