first commit
This commit is contained in:
239
pages/index/DownloadIcon.vue
Normal file
239
pages/index/DownloadIcon.vue
Normal file
@@ -0,0 +1,239 @@
|
||||
<template>
|
||||
<div class="p-6 max-w-3xl mx-auto bg-white rounded-xl shadow-lg space-y-6">
|
||||
<h2 class="text-2xl font-bold text-gray-900">帮提服务图标生成器</h2>
|
||||
|
||||
<!-- Icon Preview -->
|
||||
<div class="flex justify-center">
|
||||
<div class="w-36 h-36 bg-gray-50 rounded-lg flex items-center justify-center">
|
||||
<svg
|
||||
ref="iconRef"
|
||||
:width="BASE_SIZE"
|
||||
:height="BASE_SIZE"
|
||||
:viewBox="`0 0 ${BASE_SIZE} ${BASE_SIZE}`"
|
||||
>
|
||||
<!-- Base layer with standard background color -->
|
||||
<rect
|
||||
:x="x"
|
||||
:y="y"
|
||||
:width="ICON_SIZE"
|
||||
:height="ICON_SIZE"
|
||||
:fill="selectedCombo.baseColor"
|
||||
:rx="CORNER_RADIUS"
|
||||
/>
|
||||
|
||||
<!-- Gradient layer -->
|
||||
<rect
|
||||
:x="x"
|
||||
:y="y"
|
||||
:width="ICON_SIZE"
|
||||
:height="ICON_SIZE"
|
||||
:fill="`url(#gradient-${selectedCombo.name})`"
|
||||
:rx="CORNER_RADIUS"
|
||||
/>
|
||||
|
||||
<!-- White expression layer with shadow -->
|
||||
<g :filter="`url(#shadow-${selectedCombo.name})`">
|
||||
<rect
|
||||
:x="x"
|
||||
:y="y"
|
||||
:width="ICON_SIZE"
|
||||
:height="ICON_SIZE"
|
||||
fill="white"
|
||||
:rx="CORNER_RADIUS"
|
||||
/>
|
||||
</g>
|
||||
|
||||
<!-- Service Icon - Two people interaction -->
|
||||
<g :transform="`translate(${x}, ${y}) scale(${SCALE})`">
|
||||
<!-- Standing Person -->
|
||||
<path
|
||||
d="M8 4C9.1 4 10 4.9 10 6C10 7.1 9.1 8 8 8C6.9 8 6 7.1 6 6C6 4.9 6.9 4 8 4ZM8 9C10.2 9 12 10.8 12 13V16H4V13C4 10.8 5.8 9 8 9Z"
|
||||
:fill="selectedCombo.shadowColor"
|
||||
/>
|
||||
|
||||
<!-- Person with Luggage -->
|
||||
<path
|
||||
d="M18 4C19.1 4 20 4.9 20 6C20 7.1 19.1 8 18 8C16.9 8 16 7.1 16 6C16 4.9 16.9 4 18 4ZM18 9C20.2 9 22 10.8 22 13V16H18V13C18 10.8 16.2 9 14 9C13.7 9 13.4 9 13.1 9.1C13.7 10 14 11 14 12V16H16V22H14V18H10V22H8V16H10V12C10 11 9.7 10 9.1 9.1C8.8 9 8.5 9 8.2 9"
|
||||
:fill="selectedCombo.shadowColor"
|
||||
/>
|
||||
|
||||
<!-- Luggage -->
|
||||
<path
|
||||
d="M18 13V20H14V13H18Z"
|
||||
:fill="selectedCombo.shadowColor"
|
||||
/>
|
||||
</g>
|
||||
|
||||
<!-- Gradient and shadow definitions -->
|
||||
<defs>
|
||||
<linearGradient
|
||||
:id="`gradient-${selectedCombo.name}`"
|
||||
x1="0"
|
||||
y1="0"
|
||||
x2="1"
|
||||
y2="1"
|
||||
>
|
||||
<stop offset="0%" :stop-color="selectedCombo.gradients[0]" />
|
||||
<stop offset="100%" :stop-color="selectedCombo.gradients[1]" />
|
||||
</linearGradient>
|
||||
<filter
|
||||
:id="`shadow-${selectedCombo.name}`"
|
||||
x="-20%"
|
||||
y="-20%"
|
||||
width="140%"
|
||||
height="140%"
|
||||
>
|
||||
<feDropShadow
|
||||
dx="0"
|
||||
:dy="SCALE * 0.5"
|
||||
:stdDeviation="SCALE * 0.5"
|
||||
:flood-color="selectedCombo.shadowColor"
|
||||
flood-opacity="0.5"
|
||||
/>
|
||||
</filter>
|
||||
</defs>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Color Combination Selection -->
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-2">颜色组合</label>
|
||||
<div class="grid grid-cols-4 gap-4">
|
||||
<button
|
||||
v-for="(combo, index) in COLOR_COMBINATIONS"
|
||||
:key="index"
|
||||
@click="setSelectedCombo(combo)"
|
||||
class="h-16 relative rounded-lg overflow-hidden"
|
||||
:class="{ 'ring-2 ring-blue-500': selectedCombo === combo }"
|
||||
>
|
||||
<div class="absolute inset-0" :style="{ backgroundColor: combo.baseColor }" />
|
||||
<div
|
||||
class="absolute inset-0"
|
||||
:style="{
|
||||
background: `linear-gradient(135deg, ${combo.gradients[0]}, ${combo.gradients[1]})`
|
||||
}"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Download Button -->
|
||||
<div class="flex justify-center">
|
||||
<button
|
||||
@click="downloadIcon"
|
||||
class="inline-flex items-center px-4 py-2 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
|
||||
>
|
||||
<download-icon class="mr-2 h-5 w-5" />
|
||||
导出图标 (PNG 108x108)
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="text-sm text-gray-500">
|
||||
规格说明:
|
||||
- 尺寸:108x108px (3倍图)
|
||||
- 格式:PNG
|
||||
- 背景:透明
|
||||
- 大小:≤120kb
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ref, reactive, computed } from 'vue';
|
||||
import { Download as DownloadIcon } from 'lucide-vue-next';
|
||||
|
||||
|
||||
const COLOR_COMBINATIONS = [
|
||||
{
|
||||
name: '蓝色组合',
|
||||
baseColor: '#F5F8FD',
|
||||
gradients: ['#46ACFF', '#1E92F0'],
|
||||
shadowColor: '#1E92F0'
|
||||
},
|
||||
{
|
||||
name: '金色组合',
|
||||
baseColor: '#F5F8FD',
|
||||
gradients: ['#FFD157', '#F2B921'],
|
||||
shadowColor: '#F2B921'
|
||||
},
|
||||
{
|
||||
name: '绿色组合',
|
||||
baseColor: '#F5F8FD',
|
||||
gradients: ['#15CC96', '#15CC96'],
|
||||
shadowColor: '#15CC96'
|
||||
},
|
||||
{
|
||||
name: '青色组合',
|
||||
baseColor: '#F5F8FD',
|
||||
gradients: ['#30DFF3', '#43ABFF'],
|
||||
shadowColor: '#43ABFF'
|
||||
}
|
||||
];
|
||||
|
||||
export default {
|
||||
components: {
|
||||
DownloadIcon
|
||||
},
|
||||
setup() {
|
||||
const selectedCombo = ref(COLOR_COMBINATIONS[0]);
|
||||
const iconRef = ref(null);
|
||||
|
||||
// 严格按照36x36基础尺寸的3倍图
|
||||
const SCALE = 3;
|
||||
const BASE_SIZE = 36 * SCALE;
|
||||
const CORNER_RADIUS = 2 * SCALE;
|
||||
const ICON_SIZE = 28 * SCALE;
|
||||
|
||||
const x = computed(() => (BASE_SIZE - ICON_SIZE) / 2);
|
||||
const y = computed(() => (BASE_SIZE - ICON_SIZE) / 2);
|
||||
|
||||
const setSelectedCombo = (combo) => {
|
||||
selectedCombo.value = combo;
|
||||
};
|
||||
|
||||
const downloadIcon = () => {
|
||||
if (!iconRef.value) return;
|
||||
const svgData = new XMLSerializer().serializeToString(iconRef.value);
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.width = 108;
|
||||
canvas.height = 108;
|
||||
const ctx = canvas.getContext('2d');
|
||||
|
||||
const img = new Image();
|
||||
img.src = 'data:image/svg+xml;base64,' + btoa(svgData);
|
||||
|
||||
img.onload = () => {
|
||||
ctx.drawImage(img, 0, 0);
|
||||
|
||||
// 创建临时canvas进行大小优化
|
||||
const tempCanvas = document.createElement('canvas');
|
||||
tempCanvas.width = 108;
|
||||
tempCanvas.height = 108;
|
||||
const tempCtx = tempCanvas.getContext('2d');
|
||||
tempCtx.drawImage(canvas, 0, 0);
|
||||
|
||||
// 导出优化后的PNG
|
||||
const link = document.createElement('a');
|
||||
link.download = 'help-service-icon.png';
|
||||
link.href = tempCanvas.toDataURL('image/png', 0.8); // 优化压缩率以确保小于120kb
|
||||
link.click();
|
||||
};
|
||||
};
|
||||
|
||||
return {
|
||||
selectedCombo,
|
||||
iconRef,
|
||||
SCALE,
|
||||
BASE_SIZE,
|
||||
CORNER_RADIUS,
|
||||
ICON_SIZE,
|
||||
x,
|
||||
y,
|
||||
COLOR_COMBINATIONS,
|
||||
setSelectedCombo,
|
||||
downloadIcon
|
||||
};
|
||||
}
|
||||
};
|
||||
</script>
|
||||
Reference in New Issue
Block a user