index.vue 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. <template>
  2. <view>
  3. <sjs src="./index.sjs" module="getOptionText" />
  4. <view
  5. class="van-picker-column custom-class"
  6. :style="'height: ' + itemHeight * visibleItemCount + 'px'"
  7. @touchstart="onTouchStart"
  8. @touchmove.stop.prevent="onTouchMove"
  9. @touchend="onTouchEnd"
  10. @touchcancel="onTouchEnd"
  11. >
  12. <view
  13. :style="
  14. 'transition: transform ' +
  15. duration +
  16. 'ms; line-height: ' +
  17. itemHeight +
  18. 'px; transform: translate3d(0, ' +
  19. (offset + (itemHeight * (visibleItemCount - 1)) / 2) +
  20. 'px, 0)'
  21. "
  22. >
  23. <view
  24. :wx:for="options"
  25. wx:for-item="option"
  26. wx:key="index"
  27. :data-index="index"
  28. :style="'height: ' + itemHeight + 'px'"
  29. :class="
  30. 'van-ellipsis van-picker-column__item ' +
  31. (option && option.disabled ? 'van-picker-column__item--disabled' : '') +
  32. ' ' +
  33. (index === currentIndex ? 'van-picker-column__item--selected active-class' : '')
  34. "
  35. @tap="onClickItem"
  36. >
  37. {{ getOptionText(option, valueKey) }}
  38. </view>
  39. </view>
  40. </view>
  41. </view>
  42. </template>
  43. <script>
  44. import { VantComponent } from '../common/component';
  45. import { isObj, range } from '../common/utils';
  46. const DEFAULT_DURATION = 200;
  47. export default {
  48. data() {
  49. return {
  50. startY: 0,
  51. offset: 0,
  52. duration: 0,
  53. startOffset: 0,
  54. options: [],
  55. currentIndex: 0,
  56. option: {
  57. disabled: ''
  58. }
  59. };
  60. },
  61. classes: ['active-class'],
  62. props: {
  63. valueKey: String,
  64. className: String,
  65. itemHeight: Number,
  66. visibleItemCount: Number,
  67. initialOptions: {
  68. type: Array,
  69. default: () => []
  70. },
  71. defaultIndex: {
  72. type: Number,
  73. default: 0
  74. }
  75. },
  76. created() {
  77. const { defaultIndex, initialOptions } = this;
  78. this.set({
  79. currentIndex: defaultIndex,
  80. options: initialOptions
  81. }).then(() => {
  82. this.setIndex(defaultIndex);
  83. });
  84. },
  85. watch: {
  86. defaultIndex: function (value) {
  87. this.setIndex(value);
  88. }
  89. },
  90. methods: {
  91. getCount() {
  92. return this.options.length;
  93. },
  94. onTouchStart(event) {
  95. this.setData({
  96. startY: event.touches[0].clientY,
  97. startOffset: this.offset,
  98. duration: 0
  99. });
  100. },
  101. onTouchMove(event) {
  102. const { data } = this;
  103. const deltaY = event.touches[0].clientY - data.startY;
  104. this.setData({
  105. offset: range(data.startOffset + deltaY, -(this.getCount() * data.itemHeight), data.itemHeight)
  106. });
  107. },
  108. onTouchEnd() {
  109. const { data } = this;
  110. if (data.offset !== data.startOffset) {
  111. this.setData({
  112. duration: DEFAULT_DURATION
  113. });
  114. const index = range(Math.round(-data.offset / data.itemHeight), 0, this.getCount() - 1);
  115. this.setIndex(index, true);
  116. }
  117. },
  118. onClickItem(event) {
  119. const { index } = event.currentTarget.dataset;
  120. this.setIndex(index, true);
  121. },
  122. adjustIndex(index) {
  123. const { data } = this;
  124. const count = this.getCount();
  125. index = range(index, 0, count);
  126. for (let i = index; i < count; i++) {
  127. if (!this.isDisabled(data.options[i])) {
  128. return i;
  129. }
  130. }
  131. for (let i = index - 1; i >= 0; i--) {
  132. if (!this.isDisabled(data.options[i])) {
  133. return i;
  134. }
  135. }
  136. },
  137. isDisabled(option) {
  138. return isObj(option) && option.disabled;
  139. },
  140. getOptionText(option) {
  141. const { data } = this;
  142. return isObj(option) && data.valueKey in option ? option[data.valueKey] : option;
  143. },
  144. setIndex(index, userAction) {
  145. const { data } = this;
  146. index = this.adjustIndex(index) || 0;
  147. const offset = -index * data.itemHeight;
  148. if (index !== data.currentIndex) {
  149. return this.set({
  150. offset,
  151. currentIndex: index
  152. }).then(() => {
  153. if (userAction) {
  154. this.$emit('change', index);
  155. }
  156. });
  157. }
  158. return this.set({
  159. offset
  160. });
  161. },
  162. setValue(value) {
  163. const { options } = this;
  164. for (let i = 0; i < options.length; i++) {
  165. if (this.getOptionText(options[i]) === value) {
  166. return this.setIndex(i);
  167. }
  168. }
  169. return Promise.resolve();
  170. },
  171. getValue() {
  172. const { data } = this;
  173. return data.options[data.currentIndex];
  174. }
  175. }
  176. };
  177. </script>
  178. <style>
  179. @import '../common/index.ttss';
  180. .van-picker-column {
  181. overflow: hidden;
  182. text-align: center;
  183. color: #000;
  184. color: var(--picker-option-text-color, #000);
  185. font-size: 16px;
  186. font-size: var(--picker-option-font-size, 16px);
  187. }
  188. .van-picker-column__item {
  189. padding: 0 5px;
  190. }
  191. .van-picker-column__item--selected {
  192. font-weight: 500;
  193. font-weight: var(--font-weight-bold, 500);
  194. color: #323233;
  195. color: var(--picker-option-selected-text-color, #323233);
  196. }
  197. .van-picker-column__item--disabled {
  198. opacity: 0.3;
  199. opacity: var(--picker-option-disabled-opacity, 0.3);
  200. }
  201. </style>