hairline.uts 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. import { unitConvert } from '@/uni_modules/lime-shared/unitConvert';
  2. export type DrawBorderOptions = {
  3. direction : 'top' | 'bottom' | 'left' | 'right';
  4. color ?: string;
  5. colorKey ?: string; // 在dom中获取颜色
  6. startOffsetKey?: string; // 在dom哪个属性获取
  7. startOffset ?: number | string; // 支持数字或 CSS 字符串(如 '10px')
  8. endOffset ?: number | string;
  9. lineWidth ?: number;
  10. watchSize ?: boolean; // 是否监听尺寸变化自动重绘
  11. immediate ?: boolean; // 是否立即绘制
  12. bordered?: boolean;
  13. }
  14. export type UseDrawBorderReturn = {
  15. color: Ref<string>,
  16. renderBorder: () => void,
  17. clearBorder: () => void;
  18. dispose: () => void,
  19. }
  20. /**
  21. * 在元素上绘制边框,并支持动态监听尺寸变化
  22. * @param elementRef 目标元素的 Ref
  23. * @param options 边框配置
  24. * @returns 清理函数(用于卸载时取消监听)
  25. */
  26. export function useDrawBorder(
  27. elementRef : Ref<UniElement | null>,
  28. options : DrawBorderOptions
  29. ):UseDrawBorderReturn {
  30. let resizeObserver : UniResizeObserver | null = null;
  31. const { watchSize = true, immediate = true } = options;
  32. const defalutColor = '#e7e7e7'
  33. const color = ref(options.color ?? defalutColor)
  34. const bordered = ref(options.bordered ?? true)
  35. let computedStartOffset = 0
  36. let computedEndOffset = 0
  37. // 绘制边框
  38. const renderBorder = () => {
  39. if (elementRef.value == null) return;
  40. const ctx = elementRef.value!.getDrawableContext();
  41. if (ctx == null) return;
  42. const rect = elementRef.value!.getBoundingClientRect();
  43. ctx.reset();
  44. const {
  45. direction,
  46. startOffset = 0,
  47. endOffset = 0,
  48. lineWidth = 0.5,
  49. colorKey,
  50. startOffsetKey,
  51. } = options;
  52. // 转换单位(如果是字符串,如 '10px')
  53. if(computedStartOffset == 0) {
  54. computedStartOffset = unitConvert((startOffsetKey != null ? elementRef.value?.style.getPropertyValue(startOffsetKey!) ?? startOffset : startOffset))
  55. }
  56. if(computedEndOffset == 0) {
  57. computedEndOffset = unitConvert(endOffset)
  58. }
  59. if(color.value == defalutColor && colorKey != null) {
  60. color.value = elementRef.value?.style.getPropertyValue(colorKey!) ?? defalutColor
  61. // if(color.value.length == 0) {
  62. // color.value = defalutColor
  63. // }
  64. }
  65. ctx.strokeStyle = color.value;
  66. ctx.lineWidth = lineWidth;
  67. // 根据方向计算坐标
  68. switch (direction) {
  69. case 'top':
  70. ctx.moveTo(computedStartOffset, 0);
  71. ctx.lineTo(rect.width - computedEndOffset, 0);
  72. break;
  73. case 'bottom':
  74. ctx.moveTo(computedStartOffset, rect.height - 0.25);
  75. ctx.lineTo(rect.width - computedEndOffset, rect.height - 0.25);
  76. break;
  77. case 'left':
  78. ctx.moveTo(0, computedStartOffset);
  79. ctx.lineTo(0, rect.height - computedEndOffset);
  80. break;
  81. case 'right':
  82. ctx.moveTo(rect.width, computedStartOffset);
  83. ctx.lineTo(rect.width, rect.height - computedEndOffset);
  84. break;
  85. }
  86. ctx.stroke();
  87. ctx.update();
  88. };
  89. const setupResizeObserver = () => {
  90. // 监听尺寸变化(如果启用)
  91. if (watchSize) {
  92. if (resizeObserver == null) {
  93. resizeObserver = new UniResizeObserver((entries : Array<UniResizeObserverEntry>) => {
  94. if(!bordered.value) return
  95. renderBorder();
  96. })
  97. }
  98. watchEffect(()=>{
  99. if (elementRef.value != null) {
  100. resizeObserver!.observe(elementRef.value!);
  101. }
  102. })
  103. }
  104. }
  105. // 清理函数(卸载时取消监听)
  106. const dispose = () => {
  107. if (resizeObserver != null && elementRef.value != null) {
  108. // resizeObserver.unobserve(elementRef.value!);
  109. resizeObserver!.disconnect();
  110. resizeObserver = null;
  111. }
  112. };
  113. const clearBorder = ()=> {
  114. if (elementRef.value == null) return;
  115. const ctx = elementRef.value!.getDrawableContext();
  116. if (ctx == null) return;
  117. bordered.value = false
  118. ctx.reset()
  119. ctx.update()
  120. }
  121. setupResizeObserver()
  122. // 初始绘制
  123. if(immediate) {
  124. renderBorder();
  125. }
  126. return {
  127. renderBorder, // 手动触发绘制
  128. dispose, // 清理监听
  129. clearBorder,
  130. color
  131. } as UseDrawBorderReturn
  132. }