index.js 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. import { VantComponent } from '../common/component';
  2. import { touch } from '../mixins/touch';
  3. import { canIUseModel } from '../common/version';
  4. import { getRect, addUnit } from '../common/utils';
  5. VantComponent({
  6. mixins: [touch],
  7. props: {
  8. range: Boolean,
  9. disabled: Boolean,
  10. useButtonSlot: Boolean,
  11. activeColor: String,
  12. inactiveColor: String,
  13. max: {
  14. type: Number,
  15. value: 100,
  16. },
  17. min: {
  18. type: Number,
  19. value: 0,
  20. },
  21. step: {
  22. type: Number,
  23. value: 1,
  24. },
  25. value: {
  26. type: null,
  27. value: 0,
  28. observer(val) {
  29. if (val !== this.value) {
  30. this.updateValue(val);
  31. }
  32. },
  33. },
  34. vertical: Boolean,
  35. barHeight: null,
  36. },
  37. created() {
  38. this.updateValue(this.data.value);
  39. },
  40. methods: {
  41. onTouchStart(event) {
  42. if (this.data.disabled)
  43. return;
  44. const { index } = event.currentTarget.dataset;
  45. if (typeof index === 'number') {
  46. this.buttonIndex = index;
  47. }
  48. this.touchStart(event);
  49. this.startValue = this.format(this.value);
  50. this.newValue = this.value;
  51. if (this.isRange(this.newValue)) {
  52. this.startValue = this.newValue.map((val) => this.format(val));
  53. }
  54. else {
  55. this.startValue = this.format(this.newValue);
  56. }
  57. this.dragStatus = 'start';
  58. },
  59. onTouchMove(event) {
  60. if (this.data.disabled)
  61. return;
  62. if (this.dragStatus === 'start') {
  63. this.$emit('drag-start');
  64. }
  65. this.touchMove(event);
  66. this.dragStatus = 'draging';
  67. getRect(this, '.van-slider').then((rect) => {
  68. const { vertical } = this.data;
  69. const delta = vertical ? this.deltaY : this.deltaX;
  70. const total = vertical ? rect.height : rect.width;
  71. const diff = (delta / total) * this.getRange();
  72. if (this.isRange(this.startValue)) {
  73. this.newValue[this.buttonIndex] =
  74. this.startValue[this.buttonIndex] + diff;
  75. }
  76. else {
  77. this.newValue = this.startValue + diff;
  78. }
  79. this.updateValue(this.newValue, false, true);
  80. });
  81. },
  82. onTouchEnd() {
  83. if (this.data.disabled)
  84. return;
  85. if (this.dragStatus === 'draging') {
  86. this.updateValue(this.newValue, true);
  87. this.$emit('drag-end');
  88. }
  89. },
  90. onClick(event) {
  91. if (this.data.disabled)
  92. return;
  93. const { min } = this.data;
  94. getRect(this, '.van-slider').then((rect) => {
  95. const { vertical } = this.data;
  96. const delta = vertical
  97. ? event.detail.y - rect.top
  98. : event.detail.x - rect.left;
  99. const total = vertical ? rect.height : rect.width;
  100. const value = Number(min) + (delta / total) * this.getRange();
  101. if (this.isRange(this.value)) {
  102. const [left, right] = this.value;
  103. const middle = (left + right) / 2;
  104. if (value <= middle) {
  105. this.updateValue([value, right], true);
  106. }
  107. else {
  108. this.updateValue([left, value], true);
  109. }
  110. }
  111. else {
  112. this.updateValue(value, true);
  113. }
  114. });
  115. },
  116. isRange(val) {
  117. const { range } = this.data;
  118. return range && Array.isArray(val);
  119. },
  120. handleOverlap(value) {
  121. if (value[0] > value[1]) {
  122. return value.slice(0).reverse();
  123. }
  124. return value;
  125. },
  126. updateValue(value, end, drag) {
  127. if (this.isRange(value)) {
  128. value = this.handleOverlap(value).map((val) => this.format(val));
  129. }
  130. else {
  131. value = this.format(value);
  132. }
  133. this.value = value;
  134. const { vertical } = this.data;
  135. const mainAxis = vertical ? 'height' : 'width';
  136. this.setData({
  137. wrapperStyle: `
  138. background: ${this.data.inactiveColor || ''};
  139. ${mainAxis}: ${addUnit(this.data.barHeight) || ''};
  140. `,
  141. barStyle: `
  142. ${mainAxis}: ${this.calcMainAxis()};
  143. left: ${vertical ? 0 : this.calcOffset()};
  144. top: ${vertical ? this.calcOffset() : 0};
  145. ${drag ? 'transition: none;' : ''}
  146. `,
  147. });
  148. if (drag) {
  149. this.$emit('drag', { value });
  150. }
  151. if (end) {
  152. this.$emit('change', value);
  153. }
  154. if ((drag || end) && canIUseModel()) {
  155. this.setData({ value });
  156. }
  157. },
  158. getScope() {
  159. return Number(this.data.max) - Number(this.data.min);
  160. },
  161. getRange() {
  162. const { max, min } = this.data;
  163. return max - min;
  164. },
  165. // 计算选中条的长度百分比
  166. calcMainAxis() {
  167. const { value } = this;
  168. const { min } = this.data;
  169. const scope = this.getScope();
  170. if (this.isRange(value)) {
  171. return `${((value[1] - value[0]) * 100) / scope}%`;
  172. }
  173. return `${((value - Number(min)) * 100) / scope}%`;
  174. },
  175. // 计算选中条的开始位置的偏移量
  176. calcOffset() {
  177. const { value } = this;
  178. const { min } = this.data;
  179. const scope = this.getScope();
  180. if (this.isRange(value)) {
  181. return `${((value[0] - Number(min)) * 100) / scope}%`;
  182. }
  183. return '0%';
  184. },
  185. format(value) {
  186. const { max, min, step } = this.data;
  187. return Math.round(Math.max(min, Math.min(value, max)) / step) * step;
  188. },
  189. },
  190. });