Skip to content

实现 CSS 效果生成器 工具

分类:developer | 标签:CSS、阴影、渐变、前端 可视化调整 box-shadow 与渐变,实时预览并生成 CSS 代码

2.1 功能说明

可视化调整 box-shadow 与渐变,实时预览并生成 CSS 代码

2.1.1 使用指南

使用指导

可视化调整各类 CSS 效果参数,实时预览并生成可直接复制的 CSS 代码,覆盖阴影、渐变、毛玻璃、圆角、文字阴影、气泡框、条纹、新拟态、按钮与 Loading 动画。

2.2 代码实现

2.2.1 组件结构

本工具是一个基于 Vue 3 <script setup> 语法的单文件组件(SFC),统一包裹在 ToolPageShell 组件内,由它提供页面标题、工具 ID、分类与「使用指南」插槽等通用外壳;核心业务逻辑(响应式状态、计算属性、事件处理函数)全部写在 <script setup> 中,输入/输出通过 el-inputel-button 等 Element Plus 组件与用户交互,所有数据均在浏览器本地处理,不会上传服务器。

2.2.2 核心逻辑概览

<script setup> 中定义的主要函数/方法:

  • hexToRgb()
  • linkR()
  • darken()
  • bubbleCss()
  • copyCss()

2.2.3 关键实现代码

以下是该工具的完整 <script setup> 实现(核心代码)。模板(<template>)与样式(<style>)已省略。

vue
import { ref, computed } from 'vue'
import { ElMessage } from 'element-plus'
import ToolPageShell from '@/components/ToolPageShell.vue'

const tab = ref('shadow')
const hexToRgb = (hex) => { const m = hex.replace('#', ''); return { r: parseInt(m.slice(0, 2), 16), g: parseInt(m.slice(2, 4), 16), b: parseInt(m.slice(4, 6), 16) } }

// 阴影
const sx = ref(0), sy = ref(8), blur = ref(24), spread = ref(0), scolor = ref('#000000'), salpha = ref(15), inset = ref(false)
const shadowCss = computed(() => {
  const a = (salpha.value / 100).toFixed(2)
  const c = hexToRgb(scolor.value)
  return `${inset.value ? 'inset ' : ''}${sx.value}px ${sy.value}px ${blur.value}px ${spread.value}px rgba(${c.r}, ${c.g}, ${c.b}, ${a})`
})

// 渐变
const gtype = ref('linear'), angle = ref(135), c1 = ref('#3883fa'), c2 = ref('#67c23a')
const gradCss = computed(() => gtype.value === 'linear' ? `linear-gradient(${angle.value}deg, ${c1.value}, ${c2.value})` : `radial-gradient(circle, ${c1.value}, ${c2.value})`)

// 毛玻璃
const gBg = ref('#3883fa'), gBgAlpha = ref(20), gBlur = ref(10), gRadius = ref(16), gBorder = ref(30)
const glassStyle = computed(() => {
  const c = hexToRgb(gBg.value)
  return {
    background: `rgba(${c.r}, ${c.g}, ${c.b}, ${(gBgAlpha.value / 100).toFixed(2)})`,
    backdropFilter: `blur(${gBlur.value}px)`,
    WebkitBackdropFilter: `blur(${gBlur.value}px)`,
    borderRadius: gRadius.value + 'px',
    border: `1px solid rgba(255,255,255, ${(gBorder.value / 100).toFixed(2)})`
  }
})

// 圆角
const rLink = ref(true), rTL = ref(12), rTR = ref(12), rBR = ref(12), rBL = ref(12)
function linkR(side) { if (!rLink.value) return; const v = { TL: rTL, TR: rTR, BR: rBR, BL: rBL }[side].value; rTL.value = rTR.value = rBR.value = rBL.value = v }
const radiusCss = computed(() => `${rTL.value}px ${rTR.value}px ${rBR.value}px ${rBL.value}px`)

// 文字阴影
const tsx = ref(2), tsy = ref(2), tblur = ref(4), tcolor = ref('#000000'), talpha = ref(60)
const textShadowCss = computed(() => {
  const c = hexToRgb(tcolor.value)
  return `${tsx.value}px ${tsy.value}px ${tblur.value}px rgba(${c.r}, ${c.g}, ${c.b}, ${(talpha.value / 100).toFixed(2)})`
})

// 气泡框
const bDir = ref('bottom'), bBg = ref('#303133'), bColor = ref('#ffffff'), bRadius = ref(8), bSize = ref(10)
const bubbleBoxStyle = computed(() => ({ background: bBg.value, color: bColor.value, borderRadius: bRadius.value + 'px' }))
const bubbleArrowStyle = computed(() => {
  const s = bSize.value, c = bBg.value
  const base = { width: '0', height: '0', position: 'absolute' }
  if (bDir.value === 'top') return { ...base, borderLeft: s + 'px solid transparent', borderRight: s + 'px solid transparent', borderBottom: s + 'px solid ' + c, top: -s + 'px', left: '20px' }
  if (bDir.value === 'bottom') return { ...base, borderLeft: s + 'px solid transparent', borderRight: s + 'px solid transparent', borderTop: s + 'px solid ' + c, bottom: -s + 'px', left: '20px' }
  if (bDir.value === 'left') return { ...base, borderTop: s + 'px solid transparent', borderBottom: s + 'px solid transparent', borderRight: s + 'px solid ' + c, left: -s + 'px', top: '50%', marginTop: -s + 'px' }
  return { ...base, borderTop: s + 'px solid transparent', borderBottom: s + 'px solid transparent', borderLeft: s + 'px solid ' + c, right: -s + 'px', top: '50%', marginTop: -s + 'px' }
})

// 条纹
const sColor1 = ref('#3883fa'), sColor2 = ref('#ffffff'), sAngle = ref(45), sWidth = ref(20)
const stripesCss = computed(() => `repeating-linear-gradient(${sAngle.value}deg, ${sColor1.value} 0, ${sColor1.value} ${sWidth.value}px, ${sColor2.value} ${sWidth.value}px, ${sColor2.value} ${sWidth.value * 2}px)`)

// 新拟态
const nBg = ref('#e0e5ec'), nRadius = ref(20), nDist = ref(10), nBlur = ref(20), nIntensity = ref(10)
const neuStyle = computed(() => {
  const c = hexToRgb(nBg.value)
  const d = nIntensity.value
  const light = `rgb(${Math.min(255, c.r + d)}, ${Math.min(255, c.g + d)}, ${Math.min(255, c.b + d)})`
  const dark = `rgb(${Math.max(0, c.r - d)}, ${Math.max(0, c.g - d)}, ${Math.max(0, c.b - d)})`
  return {
    background: nBg.value,
    borderRadius: nRadius.value + 'px',
    boxShadow: `${nDist.value}px ${nDist.value}px ${nBlur.value}px ${dark}, -${nDist.value}px -${nDist.value}px ${nBlur.value}px ${light}`
  }
})

// 按钮
const btnBg = ref('#3883fa'), btnColor = ref('#ffffff'), btnRadius = ref(8), btnPadX = ref(18), btnPadY = ref(9)
const darken = (hex, p) => { const c = hexToRgb(hex); const f = 1 - p / 100; return `rgb(${Math.round(c.r * f)}, ${Math.round(c.g * f)}, ${Math.round(c.b * f)})` }
const btnStyle = computed(() => ({ background: btnBg.value, color: btnColor.value, borderRadius: btnRadius.value + 'px', padding: `${btnPadY.value}px ${btnPadX.value}px`, border: 'none' }))
// 按钮 hover 仅作示意,复制代码里给出 :hover
const btnHover = computed(() => darken(btnBg.value, 12))

// Loading
const lType = ref('ring'), lColor = ref('#3883fa'), lSize = ref(40)
const loadingStyle = computed(() => ({
  width: lSize.value + 'px', height: lSize.value + 'px',
  border: `${Math.max(2, lSize.value / 10)}px solid rgba(0,0,0,0.1)`,
  borderTopColor: lColor.value, borderRadius: '50%', display: 'inline-block'
}))

const cssText = computed(() => {
  switch (tab.value) {
    case 'shadow': return `.box {\n  box-shadow: ${shadowCss.value};\n}`
    case 'gradient': return `.box {\n  background: ${gradCss.value};\n}`
    case 'glass': return `.glass {\n  background: ${glassStyle.value.background};\n  backdrop-filter: blur(${gBlur.value}px);\n  -webkit-backdrop-filter: blur(${gBlur.value}px);\n  border-radius: ${gRadius.value}px;\n  border: 1px solid rgba(255,255,255, ${(gBorder.value / 100).toFixed(2)});\n}`
    case 'radius': return `.box {\n  border-radius: ${radiusCss.value};\n}`
    case 'textshadow': return `.text {\n  text-shadow: ${textShadowCss.value};\n}`
    case 'bubble': return `.bubble {\n  position: relative;\n  background: ${bBg.value};\n  color: ${bColor.value};\n  border-radius: ${bRadius.value}px;\n  padding: 10px 14px;\n}\n.bubble::after {\n  content: '';\n  position: absolute;\n  ${bubbleCss()}\n}`
    case 'stripes': return `.stripes {\n  background: ${stripesCss.value};\n}`
    case 'neu': return `.neu {\n  background: ${nBg.value};\n  border-radius: ${nRadius.value}px;\n  box-shadow: ${neuStyle.value.boxShadow};\n}`
    case 'button': return `.btn {\n  background: ${btnBg.value};\n  color: ${btnColor.value};\n  border: none;\n  border-radius: ${btnRadius.value}px;\n  padding: ${btnPadY.value}px ${btnPadX.value}px;\n  cursor: pointer;\n}\n.btn:hover { background: ${btnHover.value}; }`
    case 'loading': return lType.value === 'ring'
      ? `.loader {\n  width: ${lSize.value}px;\n  height: ${lSize.value}px;\n  border: ${Math.max(2, lSize.value / 10)}px solid rgba(0,0,0,0.1);\n  border-top-color: ${lColor.value};\n  border-radius: 50%;\n  animation: spin 1s linear infinite;\n}\n@keyframes spin { to { transform: rotate(360deg); } }`
      : `.loader {\n  width: ${lSize.value / 4}px;\n  height: ${lSize.value / 4}px;\n  background: ${lColor.value};\n  border-radius: 50%;\n  animation: bounce 0.6s infinite alternate;\n}\n@keyframes bounce { to { transform: translateY(-${lSize.value / 3}px); } }`
    default: return ''
  }
})
function bubbleCss() {
  const s = bSize.value, c = bBg.value
  if (bDir.value === 'top') return `top: -${s}px; left: 20px;\n  border-left: ${s}px solid transparent;\n  border-right: ${s}px solid transparent;\n  border-bottom: ${s}px solid ${c};`
  if (bDir.value === 'bottom') return `bottom: -${s}px; left: 20px;\n  border-left: ${s}px solid transparent;\n  border-right: ${s}px solid transparent;\n  border-top: ${s}px solid ${c};`
  if (bDir.value === 'left') return `left: -${s}px; top: 50%; margin-top: -${s}px;\n  border-top: ${s}px solid transparent;\n  border-bottom: ${s}px solid transparent;\n  border-right: ${s}px solid ${c};`
  return `right: -${s}px; top: 50%; margin-top: -${s}px;\n  border-top: ${s}px solid transparent;\n  border-bottom: ${s}px solid transparent;\n  border-left: ${s}px solid ${c};`
}

function copyCss() {
  if (!cssText.value) return
  navigator.clipboard?.writeText(cssText.value).then(() => ElMessage.success('已复制 CSS'))
}

2.3 效果截图

CSS 效果生成器 效果截图

工具访问地址:https://www.i91tools.com/tools/css-generator