form-ruler.ts 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107
  1. // components/form-ruler/form-ruler.ts
  2. type Axis = {
  3. value: number;
  4. type?: 'integer' | 'median' | 'tick';
  5. }[]
  6. const prefix = 't-' as const;
  7. const createAxis = (min = 0, max = 10, precision = 0.1) => {
  8. // const length = precision.toString().match(/\d+(?:\.(\d*))/)?.[1]?.length ?? 0
  9. // 计算精度的倒数(即1除以精度),然后乘以10的幂来得到一个整数
  10. const scale = Math.pow(10, Math.ceil(Math.log10(1 / precision)));
  11. const _min = min * scale;
  12. const _max = Math.floor(max * scale);
  13. const _precision = precision * scale;
  14. const ticks: Axis = [];
  15. let current = _min;
  16. while (current < _max + _precision) {
  17. ticks.push({
  18. value: current / scale,
  19. type: Math.floor(current) % 10 ? Math.floor(current) % 5 ? 'tick' : 'median' : 'integer',
  20. });
  21. current += _precision;
  22. }
  23. return ticks;
  24. }
  25. Component({
  26. options: {
  27. multipleSlots: true,
  28. },
  29. properties: {
  30. value: { type: String, value: '' },
  31. min: { type: Number, value: 0 },
  32. max: { type: Number, value: 1 },
  33. precision: { type: Number, value: 0.1 },
  34. defaultValue: { type: Number, value: Number.NaN },
  35. line: { type: Array, value: [] },
  36. },
  37. observers: {
  38. 'precision'(precision: number) {
  39. const scale = Math.pow(10, Math.ceil(Math.log10(1 / precision)));
  40. this.setData({ scale });
  41. },
  42. 'min, max, precision'(...args: [number, number, number]) {
  43. this._updateAxis(args);
  44. },
  45. 'defaultValue, min, max, precision, line'(value, min, max, precision, line) {
  46. const offset = precision >= 1 ? precision : 0
  47. if (!value || Number.isNaN(+value)) {
  48. if (Array.isArray(line)) {
  49. min = line[0]?.value ?? min;
  50. max = line[1]?.value ?? max;
  51. }
  52. value = Math.floor((max - min - offset) / 2 + min)
  53. }
  54. this._scrollValue(value);
  55. }
  56. },
  57. lifetimes: {
  58. attached() {
  59. this._updateRect();
  60. },
  61. },
  62. data: {
  63. prefix,
  64. initialValue: '', scale: 0,
  65. axis: [] as Axis,
  66. rect: { width: 0, height: 0, gap: 5 }
  67. },
  68. /**
  69. * 组件的方法列表
  70. */
  71. methods: {
  72. _updateAxis(params: [number, number, number]) {
  73. const axis = createAxis.apply(null, params);
  74. this.setData({ axis })
  75. },
  76. _updateRect() {
  77. this.createSelectorQuery()
  78. .select('.form-ruler')
  79. .boundingClientRect()
  80. .exec(res => {
  81. const rect = res[0] as WechatMiniprogram.BoundingClientRectResult;
  82. const width = rect.width - 2;
  83. const height = rect.height - 2;
  84. this.setData({ 'rect.width': width, 'rect.height': height, });
  85. })
  86. },
  87. _updateValue(index?: number) {
  88. const value = this.data.axis[index ?? 0]?.value
  89. this.setData({ value: value.toFixed(Math.log10(this.data.scale)) })
  90. },
  91. _scrollValue(value: number | string) {
  92. const index = this.data.axis.findIndex(item => item.value === value);
  93. clearTimeout((this as any).lock);
  94. (this as any).lock = setTimeout(() => { this.setData({ initialValue: `${prefix}${index}` }); }, 300);
  95. },
  96. onScrollUpdate(event: WechatMiniprogram.ScrollViewScroll) {
  97. const left = event.detail.scrollLeft;
  98. if (left >= 0) { this._updateValue(Math.floor(left / this.data.rect.gap)); }
  99. },
  100. onScrollEnd() {
  101. this._scrollValue(this.data.value);
  102. }
  103. }
  104. })