icon.vue 1.5 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556
  1. <template>
  2. <span>
  3. <svg
  4. class="icon"
  5. aria-hidden="true"
  6. :style="{
  7. fontSize: computedSize,
  8. stroke: computedColor,
  9. fill: computedColor,
  10. display: 'inline-block',
  11. verticalAlign: 'middle',
  12. strokeWidth: strokeWidth + 'px',
  13. }"
  14. :width="computedSize"
  15. :height="computedSize"
  16. ref="svg"
  17. >
  18. <use :xlink:href="`#icon-${name}`" />
  19. </svg>
  20. </span>
  21. </template>
  22. <script lang="ts" setup>
  23. import { computed, ref, watch, watchEffect } from "vue";
  24. const props = defineProps<{
  25. name: string;
  26. percentage?: number;
  27. size?: string;
  28. color?: string;
  29. }>();
  30. // 计算属性:优先使用 props 传入的值,否则继承父级
  31. const computedSize = computed(() => props.size || "1em");
  32. const computedColor = computed(() => props.color || "currentColor");
  33. const svg = ref<SVGSVGElement | null>(null);
  34. const strokeWidth = ref<number>();
  35. const refreshStrokeWidth = () => {
  36. if (!props.percentage) {
  37. strokeWidth.value = undefined;
  38. return;
  39. }
  40. if (!svg.value) return;
  41. const symol = document.querySelector("#icon-" + props.name);
  42. if (!symol) return;
  43. const viewBox = symol.getAttribute("viewBox");
  44. if (!viewBox) return;
  45. const parts = viewBox.split(" ");
  46. if (parts.length === 4) {
  47. const size = Math.max(parseFloat(parts[2]), parseFloat(parts[3]));
  48. strokeWidth.value = (props.percentage / 100) * size; // 设定为视图高度的 2%
  49. }
  50. };
  51. watchEffect(refreshStrokeWidth);
  52. watch(props, () => setTimeout(refreshStrokeWidth), { deep: true, flush: "post" });
  53. </script>