工具完成
This commit is contained in:
332
src/components/tools/CssGradientGenerator.vue
Normal file
332
src/components/tools/CssGradientGenerator.vue
Normal file
@ -0,0 +1,332 @@
|
||||
<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>
|
Reference in New Issue
Block a user