Files
utils/src/components/tools/CssGradientGenerator.vue
zguiy 8400dbfab9
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
工具完成
2025-06-28 22:38:49 +08:00

332 lines
9.4 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="space-y-6">
<!-- 标题和描述 -->
<div class="card p-6">
<h2 class="text-2xl font-bold text-primary mb-4">CSS渐变生成器</h2>
<p class="text-secondary mb-4">创建线性渐变和径向渐变生成CSS代码并实时预览效果</p>
</div>
<!-- 渐变类型选择 -->
<div class="card p-6">
<h3 class="text-lg font-semibold text-primary mb-4">渐变类型</h3>
<div class="flex gap-3">
<button
@click="gradientType = 'linear'"
:class="gradientType === 'linear' ? 'btn-primary' : 'btn-secondary'"
class="px-4 py-2 rounded-lg"
>
线性渐变
</button>
<button
@click="gradientType = 'radial'"
:class="gradientType === 'radial' ? 'btn-primary' : 'btn-secondary'"
class="px-4 py-2 rounded-lg"
>
径向渐变
</button>
</div>
</div>
<!-- 线性渐变设置 -->
<div v-if="gradientType === 'linear'" class="card p-6">
<h3 class="text-lg font-semibold text-primary mb-4">角度设置</h3>
<div class="flex items-center gap-4">
<input
v-model.number="linearAngle"
type="range"
min="0"
max="360"
class="flex-1"
/>
<input
v-model.number="linearAngle"
type="number"
min="0"
max="360"
class="input w-20"
/>
<span>°</span>
</div>
</div>
<!-- 径向渐变设置 -->
<div v-if="gradientType === 'radial'" class="card p-6">
<h3 class="text-lg font-semibold text-primary mb-4">径向渐变设置</h3>
<!-- 形状 -->
<div class="mb-4">
<label class="block text-sm font-medium text-secondary mb-2">形状</label>
<div class="flex gap-3">
<button
v-for="shape in radialShapes"
:key="shape.value"
@click="radialShape = shape.value"
:class="{
'btn-primary': radialShape === shape.value,
'btn-secondary': radialShape !== shape.value
}"
class="px-4 py-2 rounded transition-colors"
>
{{ shape.label }}
</button>
</div>
</div>
<!-- 大小 -->
<div class="mb-4">
<label class="block text-sm font-medium text-secondary mb-2">大小</label>
<div class="grid grid-cols-2 gap-2">
<button
v-for="size in radialSizes"
:key="size.value"
@click="radialSize = size.value"
:class="{
'btn-primary': radialSize === size.value,
'btn-secondary': radialSize !== size.value
}"
class="px-3 py-2 text-sm rounded transition-colors"
>
{{ size.label }}
</button>
</div>
</div>
<!-- 位置 -->
<div class="mb-4">
<label class="block text-sm font-medium text-secondary mb-2">位置</label>
<div class="grid grid-cols-3 gap-2">
<button
v-for="position in radialPositions"
:key="position.value"
@click="radialPosition = position.value"
:class="{
'btn-primary': radialPosition === position.value,
'btn-secondary': radialPosition !== position.value
}"
class="px-3 py-2 text-sm rounded transition-colors"
>
{{ position.label }}
</button>
</div>
</div>
</div>
<!-- 颜色设置 -->
<div class="card p-6">
<h3 class="text-lg font-semibold text-primary mb-4">颜色设置</h3>
<div class="space-y-3 mb-4">
<div v-for="(color, index) in colors" :key="index" class="flex items-center gap-3">
<input
v-model="color.color"
type="color"
class="w-12 h-10 rounded"
/>
<input
v-model="color.color"
type="text"
class="input flex-1"
/>
<input
v-model.number="color.position"
type="number"
min="0"
max="100"
class="input w-20"
/>
<span>%</span>
<button
@click="removeColor(index)"
:disabled="colors.length <= 2"
class="btn-secondary"
>
删除
</button>
</div>
</div>
<button @click="addColor" class="btn-primary">添加颜色</button>
</div>
<!-- 预览 -->
<div class="card p-6">
<h3 class="text-lg font-semibold text-primary mb-4">预览</h3>
<div
class="w-full h-40 rounded-lg border"
:style="{ background: generatedCSS }"
></div>
</div>
<!-- 生成的CSS -->
<div class="card p-6">
<div class="flex items-center justify-between mb-4">
<h3 class="text-lg font-semibold text-primary">生成的CSS</h3>
<button @click="copyCSS" class="btn-secondary">复制</button>
</div>
<textarea
:value="generatedCSS"
readonly
class="input w-full h-20 font-mono"
></textarea>
</div>
<!-- 使用说明 -->
<div class="card p-6">
<h3 class="text-lg font-semibold text-primary mb-4">使用说明</h3>
<div class="prose text-secondary max-w-none">
<ul class="space-y-2">
<li><strong>线性渐变</strong>沿直线方向的颜色过渡可调整角度和方向</li>
<li><strong>径向渐变</strong>从中心点向外辐射的颜色过渡可调整形状大小和位置</li>
<li><strong>颜色设置</strong>点击颜色块选择颜色调整位置百分比控制渐变分布</li>
<li><strong>预设渐变</strong>提供多种常用渐变效果点击即可应用</li>
<li><strong>实时预览</strong>所有修改都会实时显示在预览区域</li>
<li><strong>代码生成</strong>自动生成标准CSS代码支持复制完整规则或仅background属性</li>
</ul>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, computed } from 'vue'
// 颜色接口
interface ColorStop {
color: string
position: number
}
// 渐变类型
const gradientTypes = [
{ label: '线性渐变', value: 'linear' },
{ label: '径向渐变', value: 'radial' }
]
// 线性渐变方向
const linearDirections = [
{ label: '向右', value: 90 },
{ label: '向左', value: 270 },
{ label: '向下', value: 180 },
{ label: '向上', value: 0 },
{ label: '右下', value: 135 },
{ label: '左上', value: 315 },
{ label: '右上', value: 45 },
{ label: '左下', value: 225 }
]
// 径向渐变形状
const radialShapes = [
{ label: '椭圆', value: 'ellipse' },
{ label: '圆形', value: 'circle' }
]
// 径向渐变大小
const radialSizes = [
{ label: '最近边', value: 'closest-side' },
{ label: '最近角', value: 'closest-corner' },
{ label: '最远边', value: 'farthest-side' },
{ label: '最远角', value: 'farthest-corner' }
]
// 径向渐变位置
const radialPositions = [
{ label: '左上', value: 'top left' },
{ label: '上方', value: 'top' },
{ label: '右上', value: 'top right' },
{ label: '左侧', value: 'left' },
{ label: '中心', value: 'center' },
{ label: '右侧', value: 'right' },
{ label: '左下', value: 'bottom left' },
{ label: '下方', value: 'bottom' },
{ label: '右下', value: 'bottom right' }
]
// 响应式状态
const gradientType = ref('linear')
const linearAngle = ref(90)
const radialShape = ref('ellipse')
const radialSize = ref('farthest-corner')
const radialPosition = ref('center')
const colors = ref<ColorStop[]>([
{ color: '#667eea', position: 0 },
{ color: '#764ba2', position: 100 }
])
// 计算生成的CSS
const generatedCSS = computed(() => {
const colorStops = colors.value
.sort((a, b) => a.position - b.position)
.map(color => `${color.color} ${color.position}%`)
.join(', ')
if (gradientType.value === 'linear') {
return `linear-gradient(${linearAngle.value}deg, ${colorStops})`
} else {
return `radial-gradient(${radialShape.value} ${radialSize.value} at ${radialPosition.value}, ${colorStops})`
}
})
// 添加颜色
const addColor = () => {
const newPosition = colors.value.length > 0
? Math.max(...colors.value.map(c => c.position)) + 10
: 50
colors.value.push({
color: '#000000',
position: Math.min(newPosition, 100)
})
}
// 删除颜色
const removeColor = (index: number) => {
if (colors.value.length > 2) {
colors.value.splice(index, 1)
}
}
// 复制CSS
const copyCSS = async () => {
try {
await navigator.clipboard.writeText(generatedCSS.value)
alert('CSS代码已复制到剪贴板')
} catch (error) {
console.error('复制失败:', error)
alert('复制失败,请手动复制')
}
}
</script>
<style scoped>
.slider {
-webkit-appearance: none;
appearance: none;
height: 6px;
border-radius: 3px;
background: linear-gradient(to right, #ddd, #ddd);
outline: none;
}
.slider::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
width: 20px;
height: 20px;
border-radius: 50%;
background: var(--primary-color);
cursor: pointer;
}
.slider::-moz-range-thumb {
width: 20px;
height: 20px;
border-radius: 50%;
background: var(--primary-color);
cursor: pointer;
border: none;
}
.prose ul {
list-style-type: disc;
padding-left: 1.5rem;
}
</style>