SCSS全局方法:大屏适配与样式工具库

96 阅读5分钟

下面是一套完整的SCSS全局方法,专门为大屏适配和高效开发设计:

全局SCSS工具文件(styles/_mixins.scss

// =============================================
// 变量定义
// =============================================

// 设计稿基准尺寸
$design-width: 1920px !default;
$design-height: 1080px !default;

// 断点设置
$breakpoints: (
  'xs': 0,
  'sm': 576px,
  'md': 768px,
  'lg': 992px,
  'xl': 1200px,
  'xxl': 1400px,
  '4k': 2560px,
  '8k': 3840px
) !default;

// 颜色系统
$colors: (
  'primary': #1890ff,
  'success': #52c41a,
  'warning': #faad14,
  'error': #f5222d,
  'info': #13c2c2,
  'white': #ffffff,
  'black': #000000,
  'transparent': transparent
) !default;

// 字体大小阶梯
$font-sizes: (
  'xs': 12px,
  'sm': 14px,
  'base': 16px,
  'lg': 18px,
  'xl': 20px,
  '2xl': 24px,
  '3xl': 30px,
  '4xl': 36px,
  '5xl': 48px,
  '6xl': 60px
) !default;

// 间距系统
$spacing: (
  '0': 0,
  '1': 4px,
  '2': 8px,
  '3': 12px,
  '4': 16px,
  '5': 20px,
  '6': 24px,
  '8': 32px,
  '10': 40px,
  '12': 48px,
  '16': 64px,
  '20': 80px,
  '24': 96px,
  '32': 128px
) !default;

// =============================================
// 响应式工具
// =============================================

// 媒体查询混合
@mixin respond-to($breakpoint) {
  @if map-has-key($breakpoints, $breakpoint) {
    $value: map-get($breakpoints, $breakpoint);
    
    @if $value == 0 {
      @content;
    } @else {
      @media screen and (min-width: $value) {
        @content;
      }
    }
  } @else {
    @warn "未知的断点: `#{$breakpoint}`. 可用的断点: #{map-keys($breakpoints)}";
  }
}

// 高DPI屏幕适配
@mixin retina {
  @media only screen and (-webkit-min-device-pixel-ratio: 2),
         only screen and (min--moz-device-pixel-ratio: 2),
         only screen and (-o-min-device-pixel-ratio: 2/1),
         only screen and (min-device-pixel-ratio: 2),
         only screen and (min-resolution: 192dpi),
         only screen and (min-resolution: 2dppx) {
    @content;
  }
}

// 横屏/竖屏检测
@mixin landscape {
  @media (orientation: landscape) {
    @content;
  }
}

@mixin portrait {
  @media (orientation: portrait) {
    @content;
  }
}

// =============================================
// 大屏适配核心方法
// =============================================

// 根据设计稿尺寸计算vw/vh
@function vw($px) {
  @return math.div($px, $design-width) * 100vw;
}

@function vh($px) {
  @return math.div($px, $design-height) * 100vh;
}

// 字体大小适配(支持最小最大限制)
@mixin responsive-font($min-size, $max-size, $min-width: 1200px, $max-width: 3840px) {
  font-size: #{$min-size}px;
  
  @media (min-width: #{$min-width}) and (max-width: #{$max-width}) {
    font-size: calc(
      #{$min-size}px + (#{$max-size} - #{$min-size}) * 
      ((100vw - #{$min-width}) / (#{$max-width} - #{$min-width}))
    );
  }
  
  @media (min-width: #{$max-width}) {
    font-size: #{$max-size}px;
  }
}

// 元素尺寸适配
@mixin responsive-size($property, $min-value, $max-value, $min-width: 1200px, $max-width: 3840px) {
  #{$property}: #{$min-value}px;
  
  @media (min-width: #{$min-width}) and (max-width: #{$max-width}) {
    #{$property}: calc(
      #{$min-value}px + (#{$max-value} - #{$min-value}) * 
      ((100vw - #{$min-width}) / (#{$max-width} - #{$min-width}))
    );
  }
  
  @media (min-width: #{$max-width}) {
    #{$property}: #{$max-value}px;
  }
}

// =============================================
// 颜色工具
// =============================================

// 获取颜色
@function color($key) {
  @if map-has-key($colors, $key) {
    @return map-get($colors, $key);
  }
  
  @warn "未知的颜色 `#{$key}`";
  @return null;
}

// 颜色透明度调整
@function color-alpha($color, $opacity) {
  @if type-of($color) == 'color' {
    @return rgba($color, $opacity);
  }
  @return unquote("rgba(var(--#{$color}), #{$opacity})");
}

// 颜色变亮/变暗
@function color-lighten($color, $amount) {
  @return lighten($color, $amount);
}

@function color-darken($color, $amount) {
  @return darken($color, $amount);
}

// 渐变生成器
@mixin gradient-linear($direction: to bottom, $colors...) {
  background: linear-gradient($direction, $colors);
}

@mixin gradient-radial($shape: circle, $colors...) {
  background: radial-gradient($shape, $colors);
}

// =============================================
// 字体工具
// =============================================

// 字体大小获取
@function font-size($key) {
  @if map-has-key($font-sizes, $key) {
    @return map-get($font-sizes, $key);
  }
  
  @warn "未知的字体大小 `#{$key}`";
  @return null;
}

// 字体族设置
@mixin font-family($family: sans-serif) {
  font-family: $family;
}

// 文字溢出省略号
@mixin text-ellipsis {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

@mixin text-ellipsis-multi($lines: 2) {
  display: -webkit-box;
  -webkit-line-clamp: $lines;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

// =============================================
// 布局工具
// =============================================

// 间距工具
@function spacing($key) {
  @if map-has-key($spacing, $key) {
    @return map-get($spacing, $key);
  }
  
  @warn "未知的间距 `#{$key}`";
  @return null;
}

// Flexbox 布局
@mixin flex-center {
  display: flex;
  justify-content: center;
  align-items: center;
}

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

@mixin flex-column {
  display: flex;
  flex-direction: column;
}

// 绝对定位居中
@mixin absolute-center {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
}

@mixin absolute-vertical {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
}

@mixin absolute-horizontal {
  position: absolute;
  left: 50%;
  transform: translateX(-50%);
}

// 尺寸设置
@mixin size($width, $height: $width) {
  width: $width;
  height: $height;
}

@mixin square($size) {
  @include size($size);
}

@mixin circle($size) {
  @include size($size);
  border-radius: 50%;
}

// =============================================
// 视觉效果工具
// =============================================

// 边框阴影
@mixin shadow($level: 1) {
  @if $level == 1 {
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24);
  } @else if $level == 2 {
    box-shadow: 0 3px 6px rgba(0, 0, 0, 0.16), 0 3px 6px rgba(0, 0, 0, 0.23);
  } @else if $level == 3 {
    box-shadow: 0 10px 20px rgba(0, 0, 0, 0.19), 0 6px 6px rgba(0, 0, 0, 0.23);
  } @else if $level == 4 {
    box-shadow: 0 14px 28px rgba(0, 0, 0, 0.25), 0 10px 10px rgba(0, 0, 0, 0.22);
  } @else if $level == 5 {
    box-shadow: 0 19px 38px rgba(0, 0, 0, 0.3), 0 15px 12px rgba(0, 0, 0, 0.22);
  }
}

// 玻璃拟态效果
@mixin glassmorphism($blur: 10px, $opacity: 0.1) {
  background: rgba(255, 255, 255, $opacity);
  backdrop-filter: blur($blur);
  border: 1px solid rgba(255, 255, 255, 0.2);
}

// 边框渐变
@mixin border-gradient($direction, $colors, $width: 2px) {
  border: $width solid transparent;
  background: linear-gradient(white, white) padding-box,
              linear-gradient($direction, $colors) border-box;
}

// 动画混合
@mixin transition($properties: all, $duration: 0.3s, $easing: ease) {
  transition: $properties $duration $easing;
}

@mixin animation($name, $duration: 1s, $timing: ease, $delay: 0s, $iteration: 1, $direction: normal) {
  animation: $name $duration $timing $delay $iteration $direction;
}

// =============================================
// 大屏专用样式
// =============================================

// 数据大屏容器
@mixin data-screen-container {
  width: 100vw;
  height: 100vh;
  overflow: hidden;
  position: relative;
  background: linear-gradient(135deg, #1a2a6c, #2a3a7c, #3a4a8c);
  
  &::before {
    content: '';
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    background: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><defs><pattern id="grid" width="10" height="10" patternUnits="userSpaceOnUse"><path d="M 10 0 L 0 0 0 10" fill="none" stroke="rgba(255,255,255,0.1)" stroke-width="0.5"/></pattern></defs><rect width="100" height="100" fill="url(%23grid)"/></svg>');
    pointer-events: none;
  }
}

// 图表面板样式
@mixin chart-panel {
  background: rgba(0, 0, 0, 0.3);
  border: 1px solid rgba(32, 255, 255, 0.2);
  border-radius: 8px;
  backdrop-filter: blur(10px);
  
  .panel-header {
    height: 40px;
    background: linear-gradient(90deg, rgba(0, 102, 204, 0.5), rgba(0, 204, 255, 0.3));
    border-bottom: 1px solid rgba(32, 255, 255, 0.2);
    @include flex-between;
    padding: 0 15px;
    
    .panel-title {
      color: white;
      font-size: font-size('lg');
      font-weight: bold;
      margin: 0;
    }
  }
}

// 发光效果
@mixin glow-effect($color: #00f2ff, $size: 10px) {
  box-shadow: 0 0 $size $color;
}

// 流光动画
@mixin flowing-light($color: rgba(0, 242, 255, 0.5)) {
  position: relative;
  overflow: hidden;
  
  &::after {
    content: '';
    position: absolute;
    top: -50%;
    left: -50%;
    width: 200%;
    height: 200%;
    background: linear-gradient(
      to bottom right,
      transparent 0%,
      $color 50%,
      transparent 100%
    );
    transform: rotate(30deg);
    animation: flowing 3s infinite linear;
  }
  
  @keyframes flowing {
    0% { transform: translateX(-100%) translateY(-100%) rotate(30deg); }
    100% { transform: translateX(100%) translateY(100%) rotate(30deg); }
  }
}

// =============================================
// 工具类生成器
// =============================================

// 生成间距工具类
@mixin generate-spacing-utilities {
  @each $key, $value in $spacing {
    .m-#{$key} { margin: $value; }
    .mx-#{$key} { margin-left: $value; margin-right: $value; }
    .my-#{$key} { margin-top: $value; margin-bottom: $value; }
    .mt-#{$key} { margin-top: $value; }
    .mr-#{$key} { margin-right: $value; }
    .mb-#{$key} { margin-bottom: $value; }
    .ml-#{$key} { margin-left: $value; }
    
    .p-#{$key} { padding: $value; }
    .px-#{$key} { padding-left: $value; padding-right: $value; }
    .py-#{$key} { padding-top: $value; padding-bottom: $value; }
    .pt-#{$key} { padding-top: $value; }
    .pr-#{$key} { padding-right: $value; }
    .pb-#{$key} { padding-bottom: $value; }
    .pl-#{$key} { padding-left: $value; }
  }
}

// 生成颜色工具类
@mixin generate-color-utilities {
  @each $key, $value in $colors {
    .text-#{$key} { color: $value; }
    .bg-#{$key} { background-color: $value; }
    .border-#{$key} { border-color: $value; }
  }
}

主样式文件(styles/main.scss

// 引入工具库
@use 'mixins' as *;

// 重置样式
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

// 基础样式
html {
  font-size: 16px;
  line-height: 1.5;
}

body {
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
  color: color('white');
  background-color: color('black');
}

// 工具类生成
@include generate-spacing-utilities;
@include generate-color-utilities;

// 响应式隐藏/显示
.hidden {
  display: none !important;
}

@each $breakpoint in map-keys($breakpoints) {
  @if $breakpoint != 'xs' {
    @include respond-to($breakpoint) {
      .hidden-#{$breakpoint} {
        display: none !important;
      }
      
      .visible-#{$breakpoint} {
        display: block !important;
      }
    }
  }
}

// 大屏专用类
.data-screen {
  @include data-screen-container;
}

.chart-panel {
  @include chart-panel;
}

.glass-effect {
  @include glassmorphism;
}

// 文字工具类
.text-ellipsis {
  @include text-ellipsis;
}

.text-center { text-align: center; }
.text-left { text-align: left; }
.text-right { text-align: right; }

// 布局工具类
.flex { display: flex; }
.flex-center { @include flex-center; }
.flex-between { @include flex-between; }
.flex-column { @include flex-column; }

// 自定义滚动条
::-webkit-scrollbar {
  width: 6px;
  height: 6px;
}

::-webkit-scrollbar-track {
  background: rgba(255, 255, 255, 0.1);
  border-radius: 3px;
}

::-webkit-scrollbar-thumb {
  background: rgba(255, 255, 255, 0.3);
  border-radius: 3px;
  
  &:hover {
    background: rgba(255, 255, 255, 0.5);
  }
}

在Vue项目中使用

1. 配置Vue(vite.config.ts

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

export default defineConfig({
  plugins: [vue()],
  css: {
    preprocessorOptions: {
      scss: {
        additionalData: `@import "@/styles/mixins.scss";`
      }
    }
  },
  resolve: {
    alias: {
      '@': '/src'
    }
  }
})

2. 在组件中使用示例

<template>
  <div class="data-screen">
    <div class="chart-panel glass-effect m-4 p-6">
      <h3 class="text-xl text-primary">销售数据</h3>
      <div class="chart-container"></div>
    </div>
  </div>
</template>

<script setup lang="ts">
// 组件逻辑
</script>

<style lang="scss" scoped>
.data-screen {
  // 使用mixin
  @include data-screen-container;
  
  .chart-panel {
    // 使用函数计算尺寸
    width: vw(400);
    height: vh(300);
    
    h3 {
      // 响应式字体
      @include responsive-font(16, 24);
      
      // 发光效果
      @include glow-effect(color('primary'), 5px);
    }
  }
}

// 响应式处理
@include respond-to('4k') {
  .chart-panel {
    width: vw(600);
    height: vh(400);
  }
}

@include respond-to('8k') {
  .chart-panel {
    width: vw(800);
    height: vh(500);
  }
}
</style>

主要特性说明

1. 大屏适配核心

  • vw()/vh()函数:基于设计稿的视口单位转换
  • 响应式字体和尺寸:平滑过渡不同分辨率
  • 高DPI屏幕支持:Retina屏优化

2. 开发效率提升

  • 统一的颜色、间距、字体系统
  • 常用的布局mixin(flex居中、绝对定位等)
  • 工具类生成器

3. 视觉效果丰富

  • 玻璃拟态效果
  • 渐变边框
  • 发光和流光动画

4. 维护性强

  • 统一的变量管理
  • 模块化设计
  • 清晰的命名规范

这套SCSS全局方法可以显著提高大屏项目的开发效率,保证样式的一致性和可维护性,同时提供优秀的视觉体验。