@tuya-miniapp/smart-ui 2.1.10-beta-1 → 2.1.11-beta-1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -19,3 +19,4 @@ export declare const clamp: (num: any, min: any, max: any) => number;
19
19
  export declare function getCurrentPage<T>(): T & WechatMiniprogram.OptionalInterface<WechatMiniprogram.Page.ILifetime> & WechatMiniprogram.Page.InstanceProperties & WechatMiniprogram.Page.InstanceMethods<WechatMiniprogram.IAnyObject> & WechatMiniprogram.Page.Data<WechatMiniprogram.IAnyObject> & WechatMiniprogram.IAnyObject;
20
20
  export declare const isPC: boolean;
21
21
  export declare const isWxWork: boolean;
22
+ export declare function replacePlaceholders(template: any, values: any): any;
@@ -84,3 +84,9 @@ export function getCurrentPage() {
84
84
  export const isPC = ['mac', 'windows'].includes(getSystemInfoSync().platform);
85
85
  // 是否企业微信
86
86
  export const isWxWork = getSystemInfoSync().environment === 'wxwork';
87
+ export function replacePlaceholders(template, values) {
88
+ // 使用正则表达式匹配占位符
89
+ return template.replace(/{{(.*?)}}/g, (match, key) => {
90
+ return values[key] || match; // 如果在values中找到key对应的值,则替换;否则保留原样
91
+ });
92
+ }
@@ -11,6 +11,24 @@ SmartComponent({
11
11
  currentDate3: new Date(2018, 0, 1),
12
12
  currentDate4: '12:00',
13
13
  loading: false,
14
+ formatterMap: {
15
+ year: '{{year}}年',
16
+ month: {
17
+ '01': 'January',
18
+ '02': 'February',
19
+ '03': 'March',
20
+ '04': 'April',
21
+ '05': 'May',
22
+ '06': 'June',
23
+ '07': 'July',
24
+ '08': 'August',
25
+ '09': 'September',
26
+ '10': 'October',
27
+ '11': 'November',
28
+ '12': 'December',
29
+ },
30
+ day: '{{day}}日'
31
+ },
14
32
  formatter(type, value) {
15
33
  if (type === 'year') {
16
34
  return `${value}${I18n.t('year')}`;
@@ -1,13 +1,11 @@
1
1
  import { SmartComponent } from '../common/component';
2
2
  import { isDef } from '../common/validator';
3
3
  import { pickerProps } from '../picker/shared';
4
+ import { range, replacePlaceholders } from '../common/utils';
4
5
  const currentYear = new Date().getFullYear();
5
6
  function isValidDate(date) {
6
7
  return isDef(date) && !isNaN(new Date(date).getTime());
7
8
  }
8
- function range(num, min, max) {
9
- return Math.min(Math.max(num, min), max);
10
- }
11
9
  function padZero(val) {
12
10
  return `00${val}`.slice(-2);
13
11
  }
@@ -73,6 +71,9 @@ SmartComponent({
73
71
  type: Number,
74
72
  value: 59,
75
73
  observer: 'updateValue',
74
+ }, formatterMap: {
75
+ type: Object,
76
+ value: undefined,
76
77
  } }),
77
78
  data: {
78
79
  innerValue: Date.now(),
@@ -98,10 +99,22 @@ SmartComponent({
98
99
  }
99
100
  return this.picker;
100
101
  },
102
+ formatterFunc(type, value) {
103
+ var _a;
104
+ const { formatterMap, formatter = defaultFormatter } = this.data;
105
+ const mapDetail = formatterMap === null || formatterMap === void 0 ? void 0 : formatterMap[type];
106
+ if (typeof mapDetail === 'string') {
107
+ return replacePlaceholders(mapDetail, { [type]: value });
108
+ }
109
+ if (typeof mapDetail === 'object') {
110
+ return (_a = mapDetail[value]) !== null && _a !== void 0 ? _a : formatter(type, value);
111
+ }
112
+ return formatter(type, value);
113
+ },
101
114
  updateColumns() {
102
- const { formatter = defaultFormatter, locale } = this.data;
115
+ const { locale } = this.data;
103
116
  const results = this.getOriginColumns().map(column => ({
104
- values: column.values.map(value => formatter(column.type, value)),
117
+ values: column.values.map(value => this.formatterFunc(column.type, value)),
105
118
  unit: locale === null || locale === void 0 ? void 0 : locale[column.type],
106
119
  }));
107
120
  return this.set({ columns: results });
@@ -264,7 +277,7 @@ SmartComponent({
264
277
  updateColumnValue(value) {
265
278
  let values = [];
266
279
  const { type } = this.data;
267
- const formatter = this.data.formatter || defaultFormatter;
280
+ const formatter = this.formatterFunc;
268
281
  const picker = this.getPicker();
269
282
  if (type === 'time') {
270
283
  const pair = value.split(':');
@@ -10,6 +10,7 @@
10
10
  visible-item-count="{{ visibleItemCount }}"
11
11
  confirm-button-text="{{ confirmButtonText }}"
12
12
  cancel-button-text="{{ cancelButtonText }}"
13
+ change-animation="{{ changeAnimation }}"
13
14
  bind:change="onChange"
14
15
  bind:confirm="onConfirm"
15
16
  bind:cancel="onCancel"
@@ -14,7 +14,7 @@ function rootClass(data) {
14
14
  var classes = ['custom-class'];
15
15
 
16
16
  if (data.classPrefix !== 'smart-icon') {
17
- classes.push('smart-icon--custom')
17
+ classes.push('smart-icon--custom');
18
18
  }
19
19
 
20
20
  if (data.classPrefix != null) {
@@ -54,6 +54,8 @@ function svgStyle(data) {
54
54
  {
55
55
  '-webkit-mask-image': imageBase64Src,
56
56
  '-webkit-mask-size': 'contain',
57
+ '-webkit-mask-repeat': 'no-repeat',
58
+ '-webkit-mask-position': 'center',
57
59
  'mask-image': imageBase64Src,
58
60
  'mask-size': 'contain',
59
61
  width: data.size,
@@ -45,6 +45,7 @@ SmartComponent({
45
45
  methods: {
46
46
  onChange1(event) {
47
47
  const { value, index } = event.detail;
48
+ console.log(`Value: ${value}, Index:${index}`);
48
49
  Toast({
49
50
  context: this,
50
51
  message: `Value: ${value}, Index:${index}`,
@@ -23,6 +23,7 @@
23
23
  active-style="{{ activeStyle }}"
24
24
  options="{{ item.values }}"
25
25
  unit="{{ item.unit || unit }}"
26
+ changeAnimation="{{ changeAnimation }}"
26
27
  default-index="{{ item.defaultIndex || defaultIndex }}"
27
28
  active-index="{{ item.activeIndex || activeIndex }}"
28
29
  item-height="{{ itemHeight }}"
@@ -18,4 +18,8 @@ export declare const pickerProps: {
18
18
  type: NumberConstructor;
19
19
  value: number;
20
20
  };
21
+ changeAnimation: {
22
+ type: BooleanConstructor;
23
+ value: boolean;
24
+ };
21
25
  };
@@ -18,4 +18,8 @@ export const pickerProps = {
18
18
  type: Number,
19
19
  value: 44,
20
20
  },
21
+ changeAnimation: {
22
+ type: Boolean,
23
+ value: true,
24
+ },
21
25
  };
@@ -1 +1 @@
1
- @import '../common/index.css';.smart-picker-column{color:var(--picker-option-selected-text-color,var(--app-B6-N1,#000));font-size:var(--picker-option-font-size,16px);font-weight:var(--font-weight-bold,500);position:relative;text-align:center}.smart-picker-column__item{padding:0 5px}.smart-picker-column__item--selected{color:var(--picker-option-selected-text-color,var(--app-B6-N1,#000));font-weight:var(--font-weight-bold,500)}.smart-picker-column__item--disabled{opacity:var(--picker-option-disabled-opacity,.3)}.smart-picker-column__unit{align-items:center;display:flex;justify-content:center;position:absolute;top:50%;transform:translateY(-50%);width:100%}.smart-picker-column__unit_text{color:var(--picker-option-unit-text-color,var(--app-B6-N4,rgba(0,0,0,.4)));font-size:var(--picker-option-unit-font-size,12px)}.smart-picker-column__unit_hidden{opacity:0}.smart-picker-column__max-text{font-weight:var(--font-weight-bold,500);opacity:0}
1
+ @import '../common/index.css';.smart-picker-column{color:var(--picker-option-selected-text-color,var(--app-B6-N1,#000));font-size:var(--picker-option-font-size,16px);font-weight:var(--font-weight-bold,500);text-align:center}.smart-picker-column,.smart-picker-column__offset{position:relative;width:100%}.smart-picker-column__visual{position:absolute;top:0;width:100%}.smart-picker-column__item{pointer-events:none}.smart-picker-column__item--selected{color:var(--picker-option-selected-text-color,var(--app-B6-N1,#000));font-weight:var(--font-weight-bold,500)}.smart-picker-column__item--disabled{opacity:var(--picker-option-disabled-opacity,.3)}.smart-picker-column__mask{background:transparent;display:flex;flex-direction:column;height:100%;position:absolute;top:0;width:100%;z-index:10}.smart-picker-column__mask__item{flex:1}.smart-picker-column__unit{align-items:center;display:flex;justify-content:center;position:absolute;top:50%;transform:translateY(-50%);width:100%}.smart-picker-column__unit_text{color:var(--picker-option-unit-text-color,var(--app-B6-N4,rgba(0,0,0,.4)));font-size:var(--picker-option-unit-font-size,12px)}.smart-picker-column__unit_hidden{opacity:0}.smart-picker-column__max-text{font-weight:var(--font-weight-bold,500);opacity:0}
@@ -1,7 +1,16 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
1
10
  import { SmartComponent } from '../common/component';
2
11
  import { range } from '../common/utils';
3
12
  import { isObj } from '../common/validator';
4
- const DEFAULT_DURATION = 200;
13
+ const DEFAULT_DURATION = 400;
5
14
  SmartComponent({
6
15
  classes: ['active-class'],
7
16
  props: {
@@ -25,11 +34,15 @@ SmartComponent({
25
34
  type: Number,
26
35
  value: 0,
27
36
  },
37
+ changeAnimation: {
38
+ type: Boolean,
39
+ value: true,
40
+ },
28
41
  activeIndex: {
29
42
  type: Number,
30
43
  value: -1,
31
44
  observer(index) {
32
- this.setIndex(index, false, true);
45
+ this.setIndex(index, false, this.data.changeAnimation);
33
46
  },
34
47
  },
35
48
  unit: {
@@ -48,57 +61,157 @@ SmartComponent({
48
61
  renderStart: 0,
49
62
  animate: false,
50
63
  maxText: '',
64
+ timer: null,
65
+ // animationIndex: -1,
66
+ preOffsetList: [],
51
67
  },
52
68
  created() {
53
69
  const { defaultIndex, activeIndex, options } = this.data;
54
70
  this.updateUint(options);
55
- this.setIndex(activeIndex !== -1 ? activeIndex : defaultIndex, false, true);
71
+ this.setIndex(activeIndex !== -1 ? activeIndex : defaultIndex, false, this.data.changeAnimation);
56
72
  },
57
73
  methods: {
58
74
  getCount() {
59
75
  return this.data.options.length;
60
76
  },
61
77
  onTouchStart(event) {
78
+ if (this.data.timer) {
79
+ clearTimeout(this.data.timer);
80
+ this.setData({
81
+ timer: null,
82
+ });
83
+ }
62
84
  this.setData({
63
85
  startY: event.touches[0].clientY,
64
86
  startOffset: this.data.offset,
65
- duration: 0,
87
+ duration: 100,
88
+ timer: null,
89
+ preOffsetList: [this.data.offset],
66
90
  });
67
91
  },
68
92
  onTouchMove(event) {
69
93
  const { data } = this;
94
+ const { preOffsetList } = data;
70
95
  const deltaY = event.touches[0].clientY - data.startY;
96
+ const offset = range(data.startOffset + deltaY, -(this.getCount() * data.itemHeight), data.itemHeight);
97
+ const direction = this.checkIsDown(offset);
98
+ // 上一次滚动的索引
99
+ const preIndex = range(Math.round(-preOffsetList[preOffsetList.length - 1] / data.itemHeight), 0, this.getCount() - 1);
100
+ // 最终定位索引
101
+ const index = range(Math.round(-offset / data.itemHeight), 0, this.getCount() - 1);
102
+ if ((direction === 'up' && index < data.renderStart + 8) ||
103
+ (direction === 'down' && index > data.renderStart + data.renderNum - 8)) {
104
+ this.updateVisibleOptions(index);
105
+ }
106
+ // 索引变化时 粗发震动反馈
107
+ if (index !== preIndex) {
108
+ // @ts-ignore
109
+ this.vibrateShort();
110
+ }
111
+ // const animationIndex = Math.abs(-offset / data.itemHeight);
71
112
  this.setData({
72
- offset: range(data.startOffset + deltaY, -(this.getCount() * data.itemHeight), data.itemHeight),
113
+ offset,
114
+ // animationIndex: animationIndex,
115
+ preOffsetList: [...data.preOffsetList, offset],
116
+ animate: false,
73
117
  });
74
118
  },
75
119
  onTouchEnd() {
120
+ return __awaiter(this, void 0, void 0, function* () {
121
+ const { data } = this;
122
+ const { preOffsetList } = data;
123
+ let preOffset = Math.max(Math.abs(preOffsetList[preOffsetList.length - 3] - preOffsetList[preOffsetList.length - 4]), Math.abs(preOffsetList[preOffsetList.length - 2] - preOffsetList[preOffsetList.length - 3]), Math.abs(preOffsetList[preOffsetList.length - 1] - preOffsetList[preOffsetList.length - 2]));
124
+ if (isNaN(preOffset))
125
+ preOffset = 0;
126
+ preOffset = Math.min(preOffset, 40);
127
+ // 三次同样的距离 说明用户一直在顶部或者底部滑动 或在move途中已经是上下边缘了
128
+ const isSameTouch = (preOffsetList[preOffsetList.length - 1] === preOffsetList[preOffsetList.length - 2] &&
129
+ preOffsetList[preOffsetList.length - 2] === preOffsetList[preOffsetList.length - 3]) ||
130
+ preOffsetList[preOffsetList.length - 1] === -(this.getCount() * data.itemHeight) ||
131
+ preOffsetList[preOffsetList.length - 1] === data.itemHeight;
132
+ // 是否是向下滚动
133
+ const direction = this.checkIsDown();
134
+ // 当滚动速度比较慢时(<3) 不增加惯性滚动距离
135
+ const offset = Math.abs(preOffset) < 3 || isSameTouch || !direction
136
+ ? data.offset
137
+ : data.offset + (direction === 'down' ? -preOffset : preOffset) * 10;
138
+ // 有数字的最大滚动距离
139
+ const countHeight = (this.getCount() - 1) * data.itemHeight;
140
+ // 动画最大滚动距离 上下各加一个 data.itemHeight 的滚动空间
141
+ const animationOffset = range(offset, -(this.getCount() * data.itemHeight), data.itemHeight);
142
+ // 最终定位滚动位置
143
+ const finOffset = animationOffset < -countHeight ? -countHeight : animationOffset > 0 ? 0 : animationOffset;
144
+ // 获取索引
145
+ const index = range(Math.round(-finOffset / data.itemHeight), 0, this.getCount() - 1);
146
+ // 获取索引的标准距离
147
+ const offsetData = -index * data.itemHeight;
148
+ // 增加惯性音效
149
+ if (Math.abs(offsetData - data.offset) > data.itemHeight && !isSameTouch) {
150
+ const countVibrate = Math.abs(offsetData - data.offset) / data.itemHeight;
151
+ // @ts-ignore
152
+ this.vibrateShort(Math.floor(countVibrate), 800);
153
+ }
154
+ // 最终定位索引
155
+ this.setData({
156
+ duration: isSameTouch ? 150 : 800,
157
+ // animationIndex: index,
158
+ offset: offsetData,
159
+ animate: true,
160
+ });
161
+ // 更新列表
162
+ if ((direction === 'up' && index < data.renderStart + 8) ||
163
+ (direction === 'down' && index > data.renderStart + data.renderNum - 8)) {
164
+ yield this.updateVisibleOptions(index);
165
+ }
166
+ // 更新索引
167
+ if (index !== data.currentIndex) {
168
+ this.setData({
169
+ timer: setTimeout(() => __awaiter(this, void 0, void 0, function* () {
170
+ this.setIndex(index, true, false);
171
+ // await this.setData({
172
+ // timer: null,
173
+ // currentIndex: index,
174
+ // // animationIndex: index,
175
+ // });
176
+ // this.$emit('change', index);
177
+ }), isSameTouch ? 150 : 800),
178
+ });
179
+ }
180
+ });
181
+ },
182
+ checkIsDown(curr) {
76
183
  const { data } = this;
77
- if (data.offset !== data.startOffset) {
78
- const index = range(Math.round(-data.offset / data.itemHeight), 0, this.getCount() - 1);
79
- this.setIndex(index, true, true);
80
- }
184
+ const { preOffsetList } = data;
185
+ const currOffset = curr === undefined ? preOffsetList[preOffsetList.length - 1] : curr;
186
+ const preOffset = curr === undefined
187
+ ? preOffsetList[preOffsetList.length - 2]
188
+ : preOffsetList[preOffsetList.length - 1];
189
+ if (currOffset === undefined || preOffset === undefined || currOffset === preOffset)
190
+ return;
191
+ return currOffset < preOffset ? 'down' : 'up';
81
192
  },
82
- onTransitionEnd() {
83
- const { options, visibleItemCount, currentIndex } = this.data;
84
- let renderNum = 0;
85
- let renderStart = 0;
86
- if (visibleItemCount < 20 && options.length > visibleItemCount) {
87
- // 选项多于20个时,进行列表优化
88
- renderNum = Math.max(visibleItemCount * 2, 20);
89
- renderStart = Math.max(0, currentIndex - renderNum / 2);
90
- const optionsV = options.slice(renderStart, renderStart + renderNum);
91
- this.setData({ optionsV, renderStart, renderNum, animate: false });
92
- }
93
- else {
94
- this.setData({ animate: false });
95
- }
96
- if (this.fireChange) {
97
- this.$emit('change', currentIndex);
193
+ vibrateShort(count, time = 1000) {
194
+ if (!count) {
195
+ // @ts-ignore
196
+ ty.vibrateShort({ type: 'light' });
197
+ return;
98
198
  }
199
+ let has = 0;
200
+ const timer = setInterval(() => {
201
+ if (has >= count) {
202
+ clearInterval(timer);
203
+ return;
204
+ }
205
+ has++;
206
+ this.vibrateShort();
207
+ }, time / count - 20);
99
208
  },
100
209
  onClickItem(event) {
101
210
  const { index } = event.currentTarget.dataset;
211
+ if (index === this.data.currentIndex || index < 0 || index > this.data.options.length - 1) {
212
+ return;
213
+ }
214
+ this.vibrateShort(Math.abs(index - this.data.currentIndex), DEFAULT_DURATION);
102
215
  this.setIndex(index, true, true);
103
216
  },
104
217
  updateUint(options) {
@@ -120,7 +233,7 @@ SmartComponent({
120
233
  if (visibleItemCount < 20 && options.length > visibleItemCount) {
121
234
  let renderNum = 0;
122
235
  let renderStart = 0;
123
- // 选项多于20个时,进行列表优化
236
+ // 选项多于 20 个时,进行列表优化
124
237
  renderNum = Math.max(visibleItemCount * 2, 20);
125
238
  renderStart = Math.max(0, targetIndex - renderNum / 2);
126
239
  const renderEnd = Math.min(options.length, renderStart + renderNum);
@@ -159,35 +272,32 @@ SmartComponent({
159
272
  const { data } = this;
160
273
  return isObj(option) && data.valueKey in option ? option[data.valueKey] : option;
161
274
  },
162
- setIndex(index, userAction, animate) {
275
+ setIndex(index, userAction, animate, time = DEFAULT_DURATION) {
163
276
  const { data } = this;
164
277
  index = this.adjustIndex(index) || 0;
165
278
  const offset = -index * data.itemHeight;
166
- this.fireChange = false;
167
279
  if (index !== data.currentIndex) {
168
280
  // 需要动画的情况下,保持最大的截取
281
+ this.updateVisibleOptions(index);
169
282
  if (animate) {
170
- return this.updateVisibleOptions(index).then(() => {
171
- this.set({
172
- currentIndex: index,
173
- offset,
174
- animate: true,
175
- duration: DEFAULT_DURATION,
176
- }).then(() => {
177
- if (!userAction)
178
- return;
179
- if ([0, data.optionsV.length - 1].includes(index)) {
180
- this.$emit('change', index);
181
- return;
182
- }
183
- this.fireChange = true;
184
- });
283
+ return this.set({
284
+ currentIndex: index,
285
+ // animationIndex: index,
286
+ offset,
287
+ animate: true,
288
+ duration: time,
289
+ }).then(() => {
290
+ if (!userAction)
291
+ return;
292
+ console.log('change', index);
293
+ this.$emit('change', index);
185
294
  });
186
295
  }
187
296
  return this.set({
188
297
  optionsV: data.options,
189
298
  offset,
190
299
  currentIndex: index,
300
+ // animationIndex: index,
191
301
  renderStart: 0,
192
302
  animate: !!animate,
193
303
  }).then(() => {
@@ -200,7 +310,7 @@ SmartComponent({
200
310
  const { options } = this.data;
201
311
  for (let i = 0; i < options.length; i++) {
202
312
  if (this.getOptionText(options[i]) === value) {
203
- return this.setIndex(i, false, true);
313
+ return this.setIndex(i, false, this.data.changeAnimation);
204
314
  }
205
315
  }
206
316
  return Promise.resolve();
@@ -9,18 +9,29 @@
9
9
  bind:touchend="onTouchEnd"
10
10
  bind:touchcancel="onTouchEnd"
11
11
  >
12
- <view style="{{ computed.wrapperStyle({ offset, itemHeight, visibleItemCount, animate, duration, renderStart, unit }) }}"
13
- bind:transitionend="onTransitionEnd"
14
- id="options">
15
- <view
16
- wx:for="{{ optionsV }}"
17
- wx:for-item="option"
12
+ <view class="smart-picker-column__offset" style="height: {{ itemHeight * options.length }}px;{{ computed.wrapperStyle({ offset, itemHeight, visibleItemCount, animate, duration, renderStart, unit }) }}" id="options">
13
+ <view class="smart-picker-column__visual" style="{{ computed.wrapperInterStyle({ offset, itemHeight, visibleItemCount, renderStart }) }}">
14
+ <view
15
+ wx:for="{{ optionsV }}"
16
+ wx:for-item="option"
17
+ wx:key="index"
18
+ data-index="{{ renderStart + index }}"
19
+ style="height: {{ itemHeight }}px;{{renderStart + index === currentIndex ? activeStyle : ''}}"
20
+ class="smart-ellipsis {{ utils.bem('picker-column__item', { disabled: option && option.disabled, selected: renderStart + index === currentIndex }) }} {{ renderStart + index === currentIndex ? 'active-class' : '' }}"
21
+ >
22
+ {{ computed.optionText(option, valueKey) }}
23
+ </view>
24
+ </view>
25
+ </view>
26
+ <view class="smart-picker-column__mask">
27
+ <view
28
+ wx:for="{{ visibleItemCount }}"
18
29
  wx:key="index"
19
- data-index="{{ renderStart + index }}"
20
- style="height: {{ itemHeight }}px;{{renderStart + index === currentIndex ? activeStyle : ''}}"
21
- class="smart-ellipsis {{ utils.bem('picker-column__item', { disabled: option && option.disabled, selected: renderStart + index === currentIndex }) }} {{ renderStart + index === currentIndex ? 'active-class' : '' }}"
22
- bindtap="onClickItem"
23
- >{{ computed.optionText(option, valueKey) }}</view>
30
+ class="smart-picker-column__mask__item"
31
+ data-index="{{ currentIndex + index - 2 }}"
32
+ bind:tap="onClickItem"
33
+ >
34
+ </view>
24
35
  </view>
25
36
  <view wx:if="{{unit}}" class="smart-picker-column__unit" style="height: {{ itemHeight }}px">
26
37
  <view class="smart-picker-column__unit_text smart-picker-column__unit_hidden" >{{unit}}</view>
@@ -20,15 +20,11 @@ function rootStyle(data) {
20
20
  function wrapperStyle(data) {
21
21
  var offset =
22
22
  data.offset + (data.itemHeight * (data.visibleItemCount - 1)) / 2;
23
- // 长列表优化
24
- if (data.renderStart) {
25
- offset += data.renderStart * data.itemHeight;
26
- }
27
23
  offset = addUnit(offset);
28
24
  if (data.animate) {
29
25
  return style({
30
26
  'text-indent': data.unit ? '-8rpx' : '0',
31
- transition: 'transform ' + data.duration + 'ms',
27
+ transition: 'transform ' + data.duration + 'ms ease-out',
32
28
  'line-height': addUnit(data.itemHeight),
33
29
  transform: 'translate3d(0, ' + offset + ', 0)',
34
30
  });
@@ -40,8 +36,29 @@ function wrapperStyle(data) {
40
36
  });
41
37
  }
42
38
 
39
+ function wrapperInterStyle(data) {
40
+ var offset = data.renderStart * data.itemHeight;
41
+ offset = addUnit(offset);
42
+ return style({
43
+ 'padding-top': offset,
44
+ });
45
+ }
46
+
47
+ // function wrapperItemStyle(data) {
48
+ // const { index, animationIndex: currentIndex } = data;
49
+ // const offsetIndex = currentIndex - index;
50
+ // const rotateX = offsetIndex * 25 > 0 ? Math.min(offsetIndex * 25, 25 * 2) : Math.max(offsetIndex * 25, -25 * 2)
51
+ // const scale = Math.min(Math.abs(offsetIndex * 0.05), 0.05 * 2)
52
+ // return style({
53
+ // transition: 'transform ' + data.duration + 'ms ease-out',
54
+ // transform: `rotateX(${rotateX}deg) scale(${1 - scale})`
55
+ // });
56
+ // }
57
+
43
58
  module.exports = {
44
59
  optionText: optionText,
45
60
  rootStyle: rootStyle,
46
61
  wrapperStyle: wrapperStyle,
62
+ wrapperInterStyle: wrapperInterStyle,
63
+ // wrapperItemStyle: wrapperItemStyle
47
64
  };
@@ -1 +1 @@
1
- @import '../common/index.wxss';.smart-picker-column{color:var(--picker-option-selected-text-color,var(--app-B6-N1,#000));font-size:var(--picker-option-font-size,16px);font-weight:var(--font-weight-bold,500);position:relative;text-align:center}.smart-picker-column__item{padding:0 5px}.smart-picker-column__item--selected{color:var(--picker-option-selected-text-color,var(--app-B6-N1,#000));font-weight:var(--font-weight-bold,500)}.smart-picker-column__item--disabled{opacity:var(--picker-option-disabled-opacity,.3)}.smart-picker-column__unit{align-items:center;display:flex;justify-content:center;position:absolute;top:50%;transform:translateY(-50%);width:100%}.smart-picker-column__unit_text{color:var(--picker-option-unit-text-color,var(--app-B6-N4,rgba(0,0,0,.4)));font-size:var(--picker-option-unit-font-size,12px)}.smart-picker-column__unit_hidden{opacity:0}.smart-picker-column__max-text{font-weight:var(--font-weight-bold,500);opacity:0}
1
+ @import '../common/index.wxss';.smart-picker-column{color:var(--picker-option-selected-text-color,var(--app-B6-N1,#000));font-size:var(--picker-option-font-size,16px);font-weight:var(--font-weight-bold,500);text-align:center}.smart-picker-column,.smart-picker-column__offset{position:relative;width:100%}.smart-picker-column__visual{position:absolute;top:0;width:100%}.smart-picker-column__item{pointer-events:none}.smart-picker-column__item--selected{color:var(--picker-option-selected-text-color,var(--app-B6-N1,#000));font-weight:var(--font-weight-bold,500)}.smart-picker-column__item--disabled{opacity:var(--picker-option-disabled-opacity,.3)}.smart-picker-column__mask{background:transparent;display:flex;flex-direction:column;height:100%;position:absolute;top:0;width:100%;z-index:10}.smart-picker-column__mask__item{flex:1}.smart-picker-column__unit{align-items:center;display:flex;justify-content:center;position:absolute;top:50%;transform:translateY(-50%);width:100%}.smart-picker-column__unit_text{color:var(--picker-option-unit-text-color,var(--app-B6-N4,rgba(0,0,0,.4)));font-size:var(--picker-option-unit-font-size,12px)}.smart-picker-column__unit_hidden{opacity:0}.smart-picker-column__max-text{font-weight:var(--font-weight-bold,500);opacity:0}
@@ -19,3 +19,4 @@ export declare const clamp: (num: any, min: any, max: any) => number;
19
19
  export declare function getCurrentPage<T>(): T & WechatMiniprogram.OptionalInterface<WechatMiniprogram.Page.ILifetime> & WechatMiniprogram.Page.InstanceProperties & WechatMiniprogram.Page.InstanceMethods<WechatMiniprogram.IAnyObject> & WechatMiniprogram.Page.Data<WechatMiniprogram.IAnyObject> & WechatMiniprogram.IAnyObject;
20
20
  export declare const isPC: boolean;
21
21
  export declare const isWxWork: boolean;
22
+ export declare function replacePlaceholders(template: any, values: any): any;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.isWxWork = exports.isPC = exports.getCurrentPage = exports.clamp = exports.addNumber = exports.toPromise = exports.groupSetData = exports.getAllRect = exports.getRect = exports.pickExclude = exports.requestAnimationFrame = exports.addUnit = exports.nextTick = exports.range = exports.getSystemInfoSync = exports.isDef = void 0;
3
+ exports.replacePlaceholders = exports.isWxWork = exports.isPC = exports.getCurrentPage = exports.clamp = exports.addNumber = exports.toPromise = exports.groupSetData = exports.getAllRect = exports.getRect = exports.pickExclude = exports.requestAnimationFrame = exports.addUnit = exports.nextTick = exports.range = exports.getSystemInfoSync = exports.isDef = void 0;
4
4
  var validator_1 = require("./validator");
5
5
  var version_1 = require("./version");
6
6
  var validator_2 = require("./validator");
@@ -107,3 +107,10 @@ exports.getCurrentPage = getCurrentPage;
107
107
  exports.isPC = ['mac', 'windows'].includes((0, version_1.getSystemInfoSync)().platform);
108
108
  // 是否企业微信
109
109
  exports.isWxWork = (0, version_1.getSystemInfoSync)().environment === 'wxwork';
110
+ function replacePlaceholders(template, values) {
111
+ // 使用正则表达式匹配占位符
112
+ return template.replace(/{{(.*?)}}/g, function (match, key) {
113
+ return values[key] || match; // 如果在values中找到key对应的值,则替换;否则保留原样
114
+ });
115
+ }
116
+ exports.replacePlaceholders = replacePlaceholders;
@@ -16,6 +16,24 @@ var toast_1 = __importDefault(require("../../toast/toast"));
16
16
  currentDate3: new Date(2018, 0, 1),
17
17
  currentDate4: '12:00',
18
18
  loading: false,
19
+ formatterMap: {
20
+ year: '{{year}}年',
21
+ month: {
22
+ '01': 'January',
23
+ '02': 'February',
24
+ '03': 'March',
25
+ '04': 'April',
26
+ '05': 'May',
27
+ '06': 'June',
28
+ '07': 'July',
29
+ '08': 'August',
30
+ '09': 'September',
31
+ '10': 'October',
32
+ '11': 'November',
33
+ '12': 'December',
34
+ },
35
+ day: '{{day}}日'
36
+ },
19
37
  formatter: function (type, value) {
20
38
  if (type === 'year') {
21
39
  return "".concat(value).concat(I18n.t('year'));
@@ -23,13 +23,11 @@ Object.defineProperty(exports, "__esModule", { value: true });
23
23
  var component_1 = require("../common/component");
24
24
  var validator_1 = require("../common/validator");
25
25
  var shared_1 = require("../picker/shared");
26
+ var utils_1 = require("../common/utils");
26
27
  var currentYear = new Date().getFullYear();
27
28
  function isValidDate(date) {
28
29
  return (0, validator_1.isDef)(date) && !isNaN(new Date(date).getTime());
29
30
  }
30
- function range(num, min, max) {
31
- return Math.min(Math.max(num, min), max);
32
- }
33
31
  function padZero(val) {
34
32
  return "00".concat(val).slice(-2);
35
33
  }
@@ -97,6 +95,9 @@ var defaultFormatter = function (type, value) {
97
95
  type: Number,
98
96
  value: 59,
99
97
  observer: 'updateValue',
98
+ }, formatterMap: {
99
+ type: Object,
100
+ value: undefined,
100
101
  } }),
101
102
  data: {
102
103
  innerValue: Date.now(),
@@ -129,10 +130,24 @@ var defaultFormatter = function (type, value) {
129
130
  }
130
131
  return this.picker;
131
132
  },
133
+ formatterFunc: function (type, value) {
134
+ var _a;
135
+ var _b;
136
+ var _c = this.data, formatterMap = _c.formatterMap, _d = _c.formatter, formatter = _d === void 0 ? defaultFormatter : _d;
137
+ var mapDetail = formatterMap === null || formatterMap === void 0 ? void 0 : formatterMap[type];
138
+ if (typeof mapDetail === 'string') {
139
+ return (0, utils_1.replacePlaceholders)(mapDetail, (_a = {}, _a[type] = value, _a));
140
+ }
141
+ if (typeof mapDetail === 'object') {
142
+ return (_b = mapDetail[value]) !== null && _b !== void 0 ? _b : formatter(type, value);
143
+ }
144
+ return formatter(type, value);
145
+ },
132
146
  updateColumns: function () {
133
- var _a = this.data, _b = _a.formatter, formatter = _b === void 0 ? defaultFormatter : _b, locale = _a.locale;
147
+ var _this = this;
148
+ var locale = this.data.locale;
134
149
  var results = this.getOriginColumns().map(function (column) { return ({
135
- values: column.values.map(function (value) { return formatter(column.type, value); }),
150
+ values: column.values.map(function (value) { return _this.formatterFunc(column.type, value); }),
136
151
  unit: locale === null || locale === void 0 ? void 0 : locale[column.type],
137
152
  }); });
138
153
  return this.set({ columns: results });
@@ -210,8 +225,8 @@ var defaultFormatter = function (type, value) {
210
225
  // time type
211
226
  if (!isDateType) {
212
227
  var _a = value.split(':'), hour = _a[0], minute = _a[1];
213
- hour = padZero(range(hour, data.minHour, data.maxHour));
214
- minute = padZero(range(minute, data.minMinute, data.maxMinute));
228
+ hour = padZero((0, utils_1.range)(hour, data.minHour, data.maxHour));
229
+ minute = padZero((0, utils_1.range)(minute, data.minMinute, data.maxMinute));
215
230
  return "".concat(hour, ":").concat(minute);
216
231
  }
217
232
  // date type
@@ -299,7 +314,7 @@ var defaultFormatter = function (type, value) {
299
314
  var _this = this;
300
315
  var values = [];
301
316
  var type = this.data.type;
302
- var formatter = this.data.formatter || defaultFormatter;
317
+ var formatter = this.formatterFunc;
303
318
  var picker = this.getPicker();
304
319
  if (type === 'time') {
305
320
  var pair = value.split(':');
@@ -10,6 +10,7 @@
10
10
  visible-item-count="{{ visibleItemCount }}"
11
11
  confirm-button-text="{{ confirmButtonText }}"
12
12
  cancel-button-text="{{ cancelButtonText }}"
13
+ change-animation="{{ changeAnimation }}"
13
14
  bind:change="onChange"
14
15
  bind:confirm="onConfirm"
15
16
  bind:cancel="onCancel"
@@ -14,7 +14,7 @@ function rootClass(data) {
14
14
  var classes = ['custom-class'];
15
15
 
16
16
  if (data.classPrefix !== 'smart-icon') {
17
- classes.push('smart-icon--custom')
17
+ classes.push('smart-icon--custom');
18
18
  }
19
19
 
20
20
  if (data.classPrefix != null) {
@@ -54,6 +54,8 @@ function svgStyle(data) {
54
54
  {
55
55
  '-webkit-mask-image': imageBase64Src,
56
56
  '-webkit-mask-size': 'contain',
57
+ '-webkit-mask-repeat': 'no-repeat',
58
+ '-webkit-mask-position': 'center',
57
59
  'mask-image': imageBase64Src,
58
60
  'mask-size': 'contain',
59
61
  width: data.size,
@@ -51,6 +51,7 @@ var toast_1 = __importDefault(require("../../toast/toast"));
51
51
  methods: {
52
52
  onChange1: function (event) {
53
53
  var _a = event.detail, value = _a.value, index = _a.index;
54
+ console.log("Value: ".concat(value, ", Index\uFF1A").concat(index));
54
55
  (0, toast_1.default)({
55
56
  context: this,
56
57
  message: "Value: ".concat(value, ", Index\uFF1A").concat(index),
@@ -23,6 +23,7 @@
23
23
  active-style="{{ activeStyle }}"
24
24
  options="{{ item.values }}"
25
25
  unit="{{ item.unit || unit }}"
26
+ changeAnimation="{{ changeAnimation }}"
26
27
  default-index="{{ item.defaultIndex || defaultIndex }}"
27
28
  active-index="{{ item.activeIndex || activeIndex }}"
28
29
  item-height="{{ itemHeight }}"
@@ -18,4 +18,8 @@ export declare const pickerProps: {
18
18
  type: NumberConstructor;
19
19
  value: number;
20
20
  };
21
+ changeAnimation: {
22
+ type: BooleanConstructor;
23
+ value: boolean;
24
+ };
21
25
  };
@@ -21,4 +21,8 @@ exports.pickerProps = {
21
21
  type: Number,
22
22
  value: 44,
23
23
  },
24
+ changeAnimation: {
25
+ type: Boolean,
26
+ value: true,
27
+ },
24
28
  };
@@ -1 +1 @@
1
- @import '../common/index.css';.smart-picker-column{color:var(--picker-option-selected-text-color,var(--app-B6-N1,#000));font-size:var(--picker-option-font-size,16px);font-weight:var(--font-weight-bold,500);position:relative;text-align:center}.smart-picker-column__item{padding:0 5px}.smart-picker-column__item--selected{color:var(--picker-option-selected-text-color,var(--app-B6-N1,#000));font-weight:var(--font-weight-bold,500)}.smart-picker-column__item--disabled{opacity:var(--picker-option-disabled-opacity,.3)}.smart-picker-column__unit{align-items:center;display:flex;justify-content:center;position:absolute;top:50%;transform:translateY(-50%);width:100%}.smart-picker-column__unit_text{color:var(--picker-option-unit-text-color,var(--app-B6-N4,rgba(0,0,0,.4)));font-size:var(--picker-option-unit-font-size,12px)}.smart-picker-column__unit_hidden{opacity:0}.smart-picker-column__max-text{font-weight:var(--font-weight-bold,500);opacity:0}
1
+ @import '../common/index.css';.smart-picker-column{color:var(--picker-option-selected-text-color,var(--app-B6-N1,#000));font-size:var(--picker-option-font-size,16px);font-weight:var(--font-weight-bold,500);text-align:center}.smart-picker-column,.smart-picker-column__offset{position:relative;width:100%}.smart-picker-column__visual{position:absolute;top:0;width:100%}.smart-picker-column__item{pointer-events:none}.smart-picker-column__item--selected{color:var(--picker-option-selected-text-color,var(--app-B6-N1,#000));font-weight:var(--font-weight-bold,500)}.smart-picker-column__item--disabled{opacity:var(--picker-option-disabled-opacity,.3)}.smart-picker-column__mask{background:transparent;display:flex;flex-direction:column;height:100%;position:absolute;top:0;width:100%;z-index:10}.smart-picker-column__mask__item{flex:1}.smart-picker-column__unit{align-items:center;display:flex;justify-content:center;position:absolute;top:50%;transform:translateY(-50%);width:100%}.smart-picker-column__unit_text{color:var(--picker-option-unit-text-color,var(--app-B6-N4,rgba(0,0,0,.4)));font-size:var(--picker-option-unit-font-size,12px)}.smart-picker-column__unit_hidden{opacity:0}.smart-picker-column__max-text{font-weight:var(--font-weight-bold,500);opacity:0}
@@ -1,9 +1,54 @@
1
1
  "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __generator = (this && this.__generator) || function (thisArg, body) {
12
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
13
+ return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
14
+ function verb(n) { return function (v) { return step([n, v]); }; }
15
+ function step(op) {
16
+ if (f) throw new TypeError("Generator is already executing.");
17
+ while (g && (g = 0, op[0] && (_ = 0)), _) try {
18
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
19
+ if (y = 0, t) op = [op[0] & 2, t.value];
20
+ switch (op[0]) {
21
+ case 0: case 1: t = op; break;
22
+ case 4: _.label++; return { value: op[1], done: false };
23
+ case 5: _.label++; y = op[1]; op = [0]; continue;
24
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
25
+ default:
26
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
27
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
28
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
29
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
30
+ if (t[2]) _.ops.pop();
31
+ _.trys.pop(); continue;
32
+ }
33
+ op = body.call(thisArg, _);
34
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
35
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
36
+ }
37
+ };
38
+ var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
39
+ if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
40
+ if (ar || !(i in from)) {
41
+ if (!ar) ar = Array.prototype.slice.call(from, 0, i);
42
+ ar[i] = from[i];
43
+ }
44
+ }
45
+ return to.concat(ar || Array.prototype.slice.call(from));
46
+ };
2
47
  Object.defineProperty(exports, "__esModule", { value: true });
3
48
  var component_1 = require("../common/component");
4
49
  var utils_1 = require("../common/utils");
5
50
  var validator_1 = require("../common/validator");
6
- var DEFAULT_DURATION = 200;
51
+ var DEFAULT_DURATION = 400;
7
52
  (0, component_1.SmartComponent)({
8
53
  classes: ['active-class'],
9
54
  props: {
@@ -27,11 +72,15 @@ var DEFAULT_DURATION = 200;
27
72
  type: Number,
28
73
  value: 0,
29
74
  },
75
+ changeAnimation: {
76
+ type: Boolean,
77
+ value: true,
78
+ },
30
79
  activeIndex: {
31
80
  type: Number,
32
81
  value: -1,
33
82
  observer: function (index) {
34
- this.setIndex(index, false, true);
83
+ this.setIndex(index, false, this.data.changeAnimation);
35
84
  },
36
85
  },
37
86
  unit: {
@@ -50,57 +99,158 @@ var DEFAULT_DURATION = 200;
50
99
  renderStart: 0,
51
100
  animate: false,
52
101
  maxText: '',
102
+ timer: null,
103
+ // animationIndex: -1,
104
+ preOffsetList: [],
53
105
  },
54
106
  created: function () {
55
107
  var _a = this.data, defaultIndex = _a.defaultIndex, activeIndex = _a.activeIndex, options = _a.options;
56
108
  this.updateUint(options);
57
- this.setIndex(activeIndex !== -1 ? activeIndex : defaultIndex, false, true);
109
+ this.setIndex(activeIndex !== -1 ? activeIndex : defaultIndex, false, this.data.changeAnimation);
58
110
  },
59
111
  methods: {
60
112
  getCount: function () {
61
113
  return this.data.options.length;
62
114
  },
63
115
  onTouchStart: function (event) {
116
+ if (this.data.timer) {
117
+ clearTimeout(this.data.timer);
118
+ this.setData({
119
+ timer: null,
120
+ });
121
+ }
64
122
  this.setData({
65
123
  startY: event.touches[0].clientY,
66
124
  startOffset: this.data.offset,
67
- duration: 0,
125
+ duration: 100,
126
+ timer: null,
127
+ preOffsetList: [this.data.offset],
68
128
  });
69
129
  },
70
130
  onTouchMove: function (event) {
71
131
  var data = this.data;
132
+ var preOffsetList = data.preOffsetList;
72
133
  var deltaY = event.touches[0].clientY - data.startY;
134
+ var offset = (0, utils_1.range)(data.startOffset + deltaY, -(this.getCount() * data.itemHeight), data.itemHeight);
135
+ var direction = this.checkIsDown(offset);
136
+ // 上一次滚动的索引
137
+ var preIndex = (0, utils_1.range)(Math.round(-preOffsetList[preOffsetList.length - 1] / data.itemHeight), 0, this.getCount() - 1);
138
+ // 最终定位索引
139
+ var index = (0, utils_1.range)(Math.round(-offset / data.itemHeight), 0, this.getCount() - 1);
140
+ if ((direction === 'up' && index < data.renderStart + 8) ||
141
+ (direction === 'down' && index > data.renderStart + data.renderNum - 8)) {
142
+ this.updateVisibleOptions(index);
143
+ }
144
+ // 索引变化时 粗发震动反馈
145
+ if (index !== preIndex) {
146
+ // @ts-ignore
147
+ this.vibrateShort();
148
+ }
149
+ // const animationIndex = Math.abs(-offset / data.itemHeight);
73
150
  this.setData({
74
- offset: (0, utils_1.range)(data.startOffset + deltaY, -(this.getCount() * data.itemHeight), data.itemHeight),
151
+ offset: offset,
152
+ // animationIndex: animationIndex,
153
+ preOffsetList: __spreadArray(__spreadArray([], data.preOffsetList, true), [offset], false),
154
+ animate: false,
75
155
  });
76
156
  },
77
157
  onTouchEnd: function () {
158
+ return __awaiter(this, void 0, void 0, function () {
159
+ var data, preOffsetList, preOffset, isSameTouch, direction, offset, countHeight, animationOffset, finOffset, index, offsetData, countVibrate;
160
+ var _this = this;
161
+ return __generator(this, function (_a) {
162
+ switch (_a.label) {
163
+ case 0:
164
+ data = this.data;
165
+ preOffsetList = data.preOffsetList;
166
+ preOffset = Math.max(Math.abs(preOffsetList[preOffsetList.length - 3] - preOffsetList[preOffsetList.length - 4]), Math.abs(preOffsetList[preOffsetList.length - 2] - preOffsetList[preOffsetList.length - 3]), Math.abs(preOffsetList[preOffsetList.length - 1] - preOffsetList[preOffsetList.length - 2]));
167
+ if (isNaN(preOffset))
168
+ preOffset = 0;
169
+ preOffset = Math.min(preOffset, 40);
170
+ isSameTouch = (preOffsetList[preOffsetList.length - 1] === preOffsetList[preOffsetList.length - 2] &&
171
+ preOffsetList[preOffsetList.length - 2] === preOffsetList[preOffsetList.length - 3]) ||
172
+ preOffsetList[preOffsetList.length - 1] === -(this.getCount() * data.itemHeight) ||
173
+ preOffsetList[preOffsetList.length - 1] === data.itemHeight;
174
+ direction = this.checkIsDown();
175
+ offset = Math.abs(preOffset) < 3 || isSameTouch || !direction
176
+ ? data.offset
177
+ : data.offset + (direction === 'down' ? -preOffset : preOffset) * 10;
178
+ countHeight = (this.getCount() - 1) * data.itemHeight;
179
+ animationOffset = (0, utils_1.range)(offset, -(this.getCount() * data.itemHeight), data.itemHeight);
180
+ finOffset = animationOffset < -countHeight ? -countHeight : animationOffset > 0 ? 0 : animationOffset;
181
+ index = (0, utils_1.range)(Math.round(-finOffset / data.itemHeight), 0, this.getCount() - 1);
182
+ offsetData = -index * data.itemHeight;
183
+ // 增加惯性音效
184
+ if (Math.abs(offsetData - data.offset) > data.itemHeight && !isSameTouch) {
185
+ countVibrate = Math.abs(offsetData - data.offset) / data.itemHeight;
186
+ // @ts-ignore
187
+ this.vibrateShort(Math.floor(countVibrate), 800);
188
+ }
189
+ // 最终定位索引
190
+ this.setData({
191
+ duration: isSameTouch ? 150 : 800,
192
+ // animationIndex: index,
193
+ offset: offsetData,
194
+ animate: true,
195
+ });
196
+ if (!((direction === 'up' && index < data.renderStart + 8) ||
197
+ (direction === 'down' && index > data.renderStart + data.renderNum - 8))) return [3 /*break*/, 2];
198
+ return [4 /*yield*/, this.updateVisibleOptions(index)];
199
+ case 1:
200
+ _a.sent();
201
+ _a.label = 2;
202
+ case 2:
203
+ // 更新索引
204
+ if (index !== data.currentIndex) {
205
+ this.setData({
206
+ timer: setTimeout(function () { return __awaiter(_this, void 0, void 0, function () {
207
+ return __generator(this, function (_a) {
208
+ this.setIndex(index, true, false);
209
+ return [2 /*return*/];
210
+ });
211
+ }); }, isSameTouch ? 150 : 800),
212
+ });
213
+ }
214
+ return [2 /*return*/];
215
+ }
216
+ });
217
+ });
218
+ },
219
+ checkIsDown: function (curr) {
78
220
  var data = this.data;
79
- if (data.offset !== data.startOffset) {
80
- var index = (0, utils_1.range)(Math.round(-data.offset / data.itemHeight), 0, this.getCount() - 1);
81
- this.setIndex(index, true, true);
82
- }
221
+ var preOffsetList = data.preOffsetList;
222
+ var currOffset = curr === undefined ? preOffsetList[preOffsetList.length - 1] : curr;
223
+ var preOffset = curr === undefined
224
+ ? preOffsetList[preOffsetList.length - 2]
225
+ : preOffsetList[preOffsetList.length - 1];
226
+ if (currOffset === undefined || preOffset === undefined || currOffset === preOffset)
227
+ return;
228
+ return currOffset < preOffset ? 'down' : 'up';
83
229
  },
84
- onTransitionEnd: function () {
85
- var _a = this.data, options = _a.options, visibleItemCount = _a.visibleItemCount, currentIndex = _a.currentIndex;
86
- var renderNum = 0;
87
- var renderStart = 0;
88
- if (visibleItemCount < 20 && options.length > visibleItemCount) {
89
- // 选项多于20个时,进行列表优化
90
- renderNum = Math.max(visibleItemCount * 2, 20);
91
- renderStart = Math.max(0, currentIndex - renderNum / 2);
92
- var optionsV = options.slice(renderStart, renderStart + renderNum);
93
- this.setData({ optionsV: optionsV, renderStart: renderStart, renderNum: renderNum, animate: false });
94
- }
95
- else {
96
- this.setData({ animate: false });
97
- }
98
- if (this.fireChange) {
99
- this.$emit('change', currentIndex);
230
+ vibrateShort: function (count, time) {
231
+ var _this = this;
232
+ if (time === void 0) { time = 1000; }
233
+ if (!count) {
234
+ // @ts-ignore
235
+ ty.vibrateShort({ type: 'light' });
236
+ return;
100
237
  }
238
+ var has = 0;
239
+ var timer = setInterval(function () {
240
+ if (has >= count) {
241
+ clearInterval(timer);
242
+ return;
243
+ }
244
+ has++;
245
+ _this.vibrateShort();
246
+ }, time / count - 20);
101
247
  },
102
248
  onClickItem: function (event) {
103
249
  var index = event.currentTarget.dataset.index;
250
+ if (index === this.data.currentIndex || index < 0 || index > this.data.options.length - 1) {
251
+ return;
252
+ }
253
+ this.vibrateShort(Math.abs(index - this.data.currentIndex), DEFAULT_DURATION);
104
254
  this.setIndex(index, true, true);
105
255
  },
106
256
  updateUint: function (options) {
@@ -122,7 +272,7 @@ var DEFAULT_DURATION = 200;
122
272
  if (visibleItemCount < 20 && options.length > visibleItemCount) {
123
273
  var renderNum = 0;
124
274
  var renderStart = 0;
125
- // 选项多于20个时,进行列表优化
275
+ // 选项多于 20 个时,进行列表优化
126
276
  renderNum = Math.max(visibleItemCount * 2, 20);
127
277
  renderStart = Math.max(0, targetIndex - renderNum / 2);
128
278
  var renderEnd = Math.min(options.length, renderStart + renderNum);
@@ -161,36 +311,34 @@ var DEFAULT_DURATION = 200;
161
311
  var data = this.data;
162
312
  return (0, validator_1.isObj)(option) && data.valueKey in option ? option[data.valueKey] : option;
163
313
  },
164
- setIndex: function (index, userAction, animate) {
314
+ setIndex: function (index, userAction, animate, time) {
165
315
  var _this = this;
316
+ if (time === void 0) { time = DEFAULT_DURATION; }
166
317
  var data = this.data;
167
318
  index = this.adjustIndex(index) || 0;
168
319
  var offset = -index * data.itemHeight;
169
- this.fireChange = false;
170
320
  if (index !== data.currentIndex) {
171
321
  // 需要动画的情况下,保持最大的截取
322
+ this.updateVisibleOptions(index);
172
323
  if (animate) {
173
- return this.updateVisibleOptions(index).then(function () {
174
- _this.set({
175
- currentIndex: index,
176
- offset: offset,
177
- animate: true,
178
- duration: DEFAULT_DURATION,
179
- }).then(function () {
180
- if (!userAction)
181
- return;
182
- if ([0, data.optionsV.length - 1].includes(index)) {
183
- _this.$emit('change', index);
184
- return;
185
- }
186
- _this.fireChange = true;
187
- });
324
+ return this.set({
325
+ currentIndex: index,
326
+ // animationIndex: index,
327
+ offset: offset,
328
+ animate: true,
329
+ duration: time,
330
+ }).then(function () {
331
+ if (!userAction)
332
+ return;
333
+ console.log('change', index);
334
+ _this.$emit('change', index);
188
335
  });
189
336
  }
190
337
  return this.set({
191
338
  optionsV: data.options,
192
339
  offset: offset,
193
340
  currentIndex: index,
341
+ // animationIndex: index,
194
342
  renderStart: 0,
195
343
  animate: !!animate,
196
344
  }).then(function () {
@@ -203,7 +351,7 @@ var DEFAULT_DURATION = 200;
203
351
  var options = this.data.options;
204
352
  for (var i = 0; i < options.length; i++) {
205
353
  if (this.getOptionText(options[i]) === value) {
206
- return this.setIndex(i, false, true);
354
+ return this.setIndex(i, false, this.data.changeAnimation);
207
355
  }
208
356
  }
209
357
  return Promise.resolve();
@@ -9,18 +9,29 @@
9
9
  bind:touchend="onTouchEnd"
10
10
  bind:touchcancel="onTouchEnd"
11
11
  >
12
- <view style="{{ computed.wrapperStyle({ offset, itemHeight, visibleItemCount, animate, duration, renderStart, unit }) }}"
13
- bind:transitionend="onTransitionEnd"
14
- id="options">
15
- <view
16
- wx:for="{{ optionsV }}"
17
- wx:for-item="option"
12
+ <view class="smart-picker-column__offset" style="height: {{ itemHeight * options.length }}px;{{ computed.wrapperStyle({ offset, itemHeight, visibleItemCount, animate, duration, renderStart, unit }) }}" id="options">
13
+ <view class="smart-picker-column__visual" style="{{ computed.wrapperInterStyle({ offset, itemHeight, visibleItemCount, renderStart }) }}">
14
+ <view
15
+ wx:for="{{ optionsV }}"
16
+ wx:for-item="option"
17
+ wx:key="index"
18
+ data-index="{{ renderStart + index }}"
19
+ style="height: {{ itemHeight }}px;{{renderStart + index === currentIndex ? activeStyle : ''}}"
20
+ class="smart-ellipsis {{ utils.bem('picker-column__item', { disabled: option && option.disabled, selected: renderStart + index === currentIndex }) }} {{ renderStart + index === currentIndex ? 'active-class' : '' }}"
21
+ >
22
+ {{ computed.optionText(option, valueKey) }}
23
+ </view>
24
+ </view>
25
+ </view>
26
+ <view class="smart-picker-column__mask">
27
+ <view
28
+ wx:for="{{ visibleItemCount }}"
18
29
  wx:key="index"
19
- data-index="{{ renderStart + index }}"
20
- style="height: {{ itemHeight }}px;{{renderStart + index === currentIndex ? activeStyle : ''}}"
21
- class="smart-ellipsis {{ utils.bem('picker-column__item', { disabled: option && option.disabled, selected: renderStart + index === currentIndex }) }} {{ renderStart + index === currentIndex ? 'active-class' : '' }}"
22
- bindtap="onClickItem"
23
- >{{ computed.optionText(option, valueKey) }}</view>
30
+ class="smart-picker-column__mask__item"
31
+ data-index="{{ currentIndex + index - 2 }}"
32
+ bind:tap="onClickItem"
33
+ >
34
+ </view>
24
35
  </view>
25
36
  <view wx:if="{{unit}}" class="smart-picker-column__unit" style="height: {{ itemHeight }}px">
26
37
  <view class="smart-picker-column__unit_text smart-picker-column__unit_hidden" >{{unit}}</view>
@@ -20,15 +20,11 @@ function rootStyle(data) {
20
20
  function wrapperStyle(data) {
21
21
  var offset =
22
22
  data.offset + (data.itemHeight * (data.visibleItemCount - 1)) / 2;
23
- // 长列表优化
24
- if (data.renderStart) {
25
- offset += data.renderStart * data.itemHeight;
26
- }
27
23
  offset = addUnit(offset);
28
24
  if (data.animate) {
29
25
  return style({
30
26
  'text-indent': data.unit ? '-8rpx' : '0',
31
- transition: 'transform ' + data.duration + 'ms',
27
+ transition: 'transform ' + data.duration + 'ms ease-out',
32
28
  'line-height': addUnit(data.itemHeight),
33
29
  transform: 'translate3d(0, ' + offset + ', 0)',
34
30
  });
@@ -40,8 +36,29 @@ function wrapperStyle(data) {
40
36
  });
41
37
  }
42
38
 
39
+ function wrapperInterStyle(data) {
40
+ var offset = data.renderStart * data.itemHeight;
41
+ offset = addUnit(offset);
42
+ return style({
43
+ 'padding-top': offset,
44
+ });
45
+ }
46
+
47
+ // function wrapperItemStyle(data) {
48
+ // const { index, animationIndex: currentIndex } = data;
49
+ // const offsetIndex = currentIndex - index;
50
+ // const rotateX = offsetIndex * 25 > 0 ? Math.min(offsetIndex * 25, 25 * 2) : Math.max(offsetIndex * 25, -25 * 2)
51
+ // const scale = Math.min(Math.abs(offsetIndex * 0.05), 0.05 * 2)
52
+ // return style({
53
+ // transition: 'transform ' + data.duration + 'ms ease-out',
54
+ // transform: `rotateX(${rotateX}deg) scale(${1 - scale})`
55
+ // });
56
+ // }
57
+
43
58
  module.exports = {
44
59
  optionText: optionText,
45
60
  rootStyle: rootStyle,
46
61
  wrapperStyle: wrapperStyle,
62
+ wrapperInterStyle: wrapperInterStyle,
63
+ // wrapperItemStyle: wrapperItemStyle
47
64
  };
@@ -1 +1 @@
1
- @import '../common/index.wxss';.smart-picker-column{color:var(--picker-option-selected-text-color,var(--app-B6-N1,#000));font-size:var(--picker-option-font-size,16px);font-weight:var(--font-weight-bold,500);position:relative;text-align:center}.smart-picker-column__item{padding:0 5px}.smart-picker-column__item--selected{color:var(--picker-option-selected-text-color,var(--app-B6-N1,#000));font-weight:var(--font-weight-bold,500)}.smart-picker-column__item--disabled{opacity:var(--picker-option-disabled-opacity,.3)}.smart-picker-column__unit{align-items:center;display:flex;justify-content:center;position:absolute;top:50%;transform:translateY(-50%);width:100%}.smart-picker-column__unit_text{color:var(--picker-option-unit-text-color,var(--app-B6-N4,rgba(0,0,0,.4)));font-size:var(--picker-option-unit-font-size,12px)}.smart-picker-column__unit_hidden{opacity:0}.smart-picker-column__max-text{font-weight:var(--font-weight-bold,500);opacity:0}
1
+ @import '../common/index.wxss';.smart-picker-column{color:var(--picker-option-selected-text-color,var(--app-B6-N1,#000));font-size:var(--picker-option-font-size,16px);font-weight:var(--font-weight-bold,500);text-align:center}.smart-picker-column,.smart-picker-column__offset{position:relative;width:100%}.smart-picker-column__visual{position:absolute;top:0;width:100%}.smart-picker-column__item{pointer-events:none}.smart-picker-column__item--selected{color:var(--picker-option-selected-text-color,var(--app-B6-N1,#000));font-weight:var(--font-weight-bold,500)}.smart-picker-column__item--disabled{opacity:var(--picker-option-disabled-opacity,.3)}.smart-picker-column__mask{background:transparent;display:flex;flex-direction:column;height:100%;position:absolute;top:0;width:100%;z-index:10}.smart-picker-column__mask__item{flex:1}.smart-picker-column__unit{align-items:center;display:flex;justify-content:center;position:absolute;top:50%;transform:translateY(-50%);width:100%}.smart-picker-column__unit_text{color:var(--picker-option-unit-text-color,var(--app-B6-N4,rgba(0,0,0,.4)));font-size:var(--picker-option-unit-font-size,12px)}.smart-picker-column__unit_hidden{opacity:0}.smart-picker-column__max-text{font-weight:var(--font-weight-bold,500);opacity:0}
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@tuya-miniapp/smart-ui",
3
- "version": "2.1.10-beta-1",
3
+ "version": "2.1.11-beta-1",
4
4
  "author": "MiniApp Team",
5
5
  "license": "MIT",
6
6
  "miniprogram": "lib",
7
7
  "description": "轻量、可靠的智能小程序 UI 组件库",
8
8
  "scripts": {
9
- "prepublishOnly": "node ./build/transCSSVar.js",
9
+ "prepublishOnly": "node ./build/prepublishOnly.js",
10
10
  "dev": "NODE_OPTIONS=--no-experimental-fetch node build/dev.mjs",
11
11
  "lint": "eslint ./packages --ext .js,.ts --fix",
12
12
  "lint:style": "stylelint \"packages/**/*.less\" --fix",
@@ -78,7 +78,7 @@
78
78
  "iOS >= 9"
79
79
  ],
80
80
  "dependencies": {
81
- "@ray-js/components-ty-slider": "^0.2.52",
81
+ "@ray-js/components-ty-slider": "^0.3.1",
82
82
  "@tuya-miniapp/icons": "^2.1.7"
83
83
  },
84
84
  "maintainers": [