index.js 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289
  1. import { createNamespace, addUnit } from '../utils';
  2. import { deepClone } from '../utils/deep-clone';
  3. import { preventDefault } from '../utils/dom/event';
  4. import { TouchMixin } from '../mixins/touch';
  5. import { FieldMixin } from '../mixins/field';
  6. var _createNamespace = createNamespace('slider'),
  7. createComponent = _createNamespace[0],
  8. bem = _createNamespace[1];
  9. var isSameValue = function isSameValue(newValue, oldValue) {
  10. return JSON.stringify(newValue) === JSON.stringify(oldValue);
  11. };
  12. export default createComponent({
  13. mixins: [TouchMixin, FieldMixin],
  14. props: {
  15. disabled: Boolean,
  16. vertical: Boolean,
  17. range: Boolean,
  18. barHeight: [Number, String],
  19. buttonSize: [Number, String],
  20. activeColor: String,
  21. inactiveColor: String,
  22. min: {
  23. type: [Number, String],
  24. default: 0
  25. },
  26. max: {
  27. type: [Number, String],
  28. default: 100
  29. },
  30. step: {
  31. type: [Number, String],
  32. default: 1
  33. },
  34. value: {
  35. type: [Number, Array],
  36. default: 0
  37. }
  38. },
  39. data: function data() {
  40. return {
  41. dragStatus: ''
  42. };
  43. },
  44. computed: {
  45. scope: function scope() {
  46. return this.max - this.min;
  47. },
  48. buttonStyle: function buttonStyle() {
  49. if (this.buttonSize) {
  50. var size = addUnit(this.buttonSize);
  51. return {
  52. width: size,
  53. height: size
  54. };
  55. }
  56. }
  57. },
  58. created: function created() {
  59. // format initial value
  60. this.updateValue(this.value);
  61. },
  62. mounted: function mounted() {
  63. if (this.range) {
  64. this.bindTouchEvent(this.$refs.wrapper0);
  65. this.bindTouchEvent(this.$refs.wrapper1);
  66. } else {
  67. this.bindTouchEvent(this.$refs.wrapper);
  68. }
  69. },
  70. methods: {
  71. onTouchStart: function onTouchStart(event) {
  72. if (this.disabled) {
  73. return;
  74. }
  75. this.touchStart(event);
  76. this.currentValue = this.value;
  77. if (this.range) {
  78. this.startValue = this.value.map(this.format);
  79. } else {
  80. this.startValue = this.format(this.value);
  81. }
  82. this.dragStatus = 'start';
  83. },
  84. onTouchMove: function onTouchMove(event) {
  85. if (this.disabled) {
  86. return;
  87. }
  88. if (this.dragStatus === 'start') {
  89. this.$emit('drag-start');
  90. }
  91. preventDefault(event, true);
  92. this.touchMove(event);
  93. this.dragStatus = 'draging';
  94. var rect = this.$el.getBoundingClientRect();
  95. var delta = this.vertical ? this.deltaY : this.deltaX;
  96. var total = this.vertical ? rect.height : rect.width;
  97. var diff = delta / total * this.scope;
  98. if (this.range) {
  99. this.currentValue[this.index] = this.startValue[this.index] + diff;
  100. } else {
  101. this.currentValue = this.startValue + diff;
  102. }
  103. this.updateValue(this.currentValue);
  104. },
  105. onTouchEnd: function onTouchEnd() {
  106. if (this.disabled) {
  107. return;
  108. }
  109. if (this.dragStatus === 'draging') {
  110. this.updateValue(this.currentValue, true);
  111. this.$emit('drag-end');
  112. }
  113. this.dragStatus = '';
  114. },
  115. onClick: function onClick(event) {
  116. event.stopPropagation();
  117. if (this.disabled) return;
  118. var rect = this.$el.getBoundingClientRect();
  119. var delta = this.vertical ? event.clientY - rect.top : event.clientX - rect.left;
  120. var total = this.vertical ? rect.height : rect.width;
  121. var value = +this.min + delta / total * this.scope;
  122. if (this.range) {
  123. var _this$value = this.value,
  124. left = _this$value[0],
  125. right = _this$value[1];
  126. var middle = (left + right) / 2;
  127. if (value <= middle) {
  128. left = value;
  129. } else {
  130. right = value;
  131. }
  132. value = [left, right];
  133. }
  134. this.startValue = this.value;
  135. this.updateValue(value, true);
  136. },
  137. // 处理两个滑块重叠之后的情况
  138. handleOverlap: function handleOverlap(value) {
  139. if (value[0] > value[1]) {
  140. value = deepClone(value);
  141. return value.reverse();
  142. }
  143. return value;
  144. },
  145. updateValue: function updateValue(value, end) {
  146. if (this.range) {
  147. value = this.handleOverlap(value).map(this.format);
  148. } else {
  149. value = this.format(value);
  150. }
  151. if (!isSameValue(value, this.value)) {
  152. this.$emit('input', value);
  153. }
  154. if (end && !isSameValue(value, this.startValue)) {
  155. this.$emit('change', value);
  156. }
  157. },
  158. format: function format(value) {
  159. return Math.round(Math.max(this.min, Math.min(value, this.max)) / this.step) * this.step;
  160. }
  161. },
  162. render: function render() {
  163. var _wrapperStyle,
  164. _this = this,
  165. _barStyle;
  166. var h = arguments[0];
  167. var vertical = this.vertical;
  168. var mainAxis = vertical ? 'height' : 'width';
  169. var crossAxis = vertical ? 'width' : 'height';
  170. var wrapperStyle = (_wrapperStyle = {
  171. background: this.inactiveColor
  172. }, _wrapperStyle[crossAxis] = addUnit(this.barHeight), _wrapperStyle); // 计算选中条的长度百分比
  173. var calcMainAxis = function calcMainAxis() {
  174. var value = _this.value,
  175. min = _this.min,
  176. range = _this.range,
  177. scope = _this.scope;
  178. if (range) {
  179. return (value[1] - value[0]) * 100 / scope + "%";
  180. }
  181. return (value - min) * 100 / scope + "%";
  182. }; // 计算选中条的开始位置的偏移量
  183. var calcOffset = function calcOffset() {
  184. var value = _this.value,
  185. min = _this.min,
  186. range = _this.range,
  187. scope = _this.scope;
  188. if (range) {
  189. return (value[0] - min) * 100 / scope + "%";
  190. }
  191. return null;
  192. };
  193. var barStyle = (_barStyle = {}, _barStyle[mainAxis] = calcMainAxis(), _barStyle.left = this.vertical ? null : calcOffset(), _barStyle.top = this.vertical ? calcOffset() : null, _barStyle.background = this.activeColor, _barStyle);
  194. if (this.dragStatus) {
  195. barStyle.transition = 'none';
  196. }
  197. var renderButton = function renderButton(i) {
  198. var map = ['left', 'right'];
  199. var isNumber = typeof i === 'number';
  200. var getClassName = function getClassName() {
  201. if (isNumber) {
  202. return "button-wrapper-" + map[i];
  203. }
  204. return "button-wrapper";
  205. };
  206. var getRefName = function getRefName() {
  207. if (isNumber) {
  208. return "wrapper" + i;
  209. }
  210. return "wrapper";
  211. };
  212. return h("div", {
  213. "ref": getRefName(),
  214. "attrs": {
  215. "role": "slider",
  216. "tabindex": _this.disabled ? -1 : 0,
  217. "aria-valuemin": _this.min,
  218. "aria-valuenow": _this.value,
  219. "aria-valuemax": _this.max,
  220. "aria-orientation": _this.vertical ? 'vertical' : 'horizontal'
  221. },
  222. "class": bem(getClassName()),
  223. "on": {
  224. "touchstart": function touchstart() {
  225. if (isNumber) {
  226. // 保存当前按钮的索引
  227. _this.index = i;
  228. }
  229. },
  230. "click": function click(e) {
  231. return e.stopPropagation();
  232. }
  233. }
  234. }, [_this.slots('button') || h("div", {
  235. "class": bem('button'),
  236. "style": _this.buttonStyle
  237. })]);
  238. };
  239. return h("div", {
  240. "style": wrapperStyle,
  241. "class": bem({
  242. disabled: this.disabled,
  243. vertical: vertical
  244. }),
  245. "on": {
  246. "click": this.onClick
  247. }
  248. }, [h("div", {
  249. "class": bem('bar'),
  250. "style": barStyle
  251. }, [this.range ? [renderButton(0), renderButton(1)] : renderButton()])]);
  252. }
  253. });