index.js 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  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 touch = event.touches[0];
  97. const delta = vertical
  98. ? touch.clientY - rect.top
  99. : touch.clientX - rect.left;
  100. const total = vertical ? rect.height : rect.width;
  101. const value = Number(min) + (delta / total) * this.getRange();
  102. if (this.isRange(this.value)) {
  103. const [left, right] = this.value;
  104. const middle = (left + right) / 2;
  105. if (value <= middle) {
  106. this.updateValue([value, right], true);
  107. }
  108. else {
  109. this.updateValue([left, value], true);
  110. }
  111. }
  112. else {
  113. this.updateValue(value, true);
  114. }
  115. });
  116. },
  117. isRange(val) {
  118. const { range } = this.data;
  119. return range && Array.isArray(val);
  120. },
  121. handleOverlap(value) {
  122. if (value[0] > value[1]) {
  123. return value.slice(0).reverse();
  124. }
  125. return value;
  126. },
  127. updateValue(value, end, drag) {
  128. if (this.isRange(value)) {
  129. value = this.handleOverlap(value).map((val) => this.format(val));
  130. }
  131. else {
  132. value = this.format(value);
  133. }
  134. this.value = value;
  135. const { vertical } = this.data;
  136. const mainAxis = vertical ? 'height' : 'width';
  137. this.setData({
  138. wrapperStyle: `
  139. background: ${this.data.inactiveColor || ''};
  140. ${vertical ? 'width' : 'height'}: ${addUnit(this.data.barHeight) || ''};
  141. `,
  142. barStyle: `
  143. ${mainAxis}: ${this.calcMainAxis()};
  144. left: ${vertical ? 0 : this.calcOffset()};
  145. top: ${vertical ? this.calcOffset() : 0};
  146. ${drag ? 'transition: none;' : ''}
  147. `,
  148. });
  149. if (drag) {
  150. this.$emit('drag', { value });
  151. }
  152. if (end) {
  153. this.$emit('change', value);
  154. }
  155. if ((drag || end) && canIUseModel()) {
  156. this.setData({ value });
  157. }
  158. },
  159. getScope() {
  160. return Number(this.data.max) - Number(this.data.min);
  161. },
  162. getRange() {
  163. const { max, min } = this.data;
  164. return max - min;
  165. },
  166. // 计算选中条的长度百分比
  167. calcMainAxis() {
  168. const { value } = this;
  169. const { min } = this.data;
  170. const scope = this.getScope();
  171. if (this.isRange(value)) {
  172. return `${((value[1] - value[0]) * 100) / scope}%`;
  173. }
  174. return `${((value - Number(min)) * 100) / scope}%`;
  175. },
  176. // 计算选中条的开始位置的偏移量
  177. calcOffset() {
  178. const { value } = this;
  179. const { min } = this.data;
  180. const scope = this.getScope();
  181. if (this.isRange(value)) {
  182. return `${((value[0] - Number(min)) * 100) / scope}%`;
  183. }
  184. return '0%';
  185. },
  186. format(value) {
  187. const { max, min, step } = this.data;
  188. return Math.round(Math.max(min, Math.min(value, max)) / step) * step;
  189. },
  190. },
  191. });