| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218 |
- <template>
- <view class="rate-box" :class="[{ animation }, containerClass]" @touchmove="ontouchmove" @touchend="touchMoving = false">
- <view
- v-for="(val, i) in list"
- :key="val"
- class="rate"
- :style="{ fontSize, paddingLeft: i !== 0 ? rateMargin : 0, paddingRight: i < list.length - 1 ? rateMargin : 0 }"
- :class="[
- { scale: !disabled && val <= rateValue && animation && touchMoving, 'color-base-text': val <= rateValue, defaultColor: val > rateValue },
- `rate-${i}`,
- rateClass
- ]"
- :data-val="val"
- @click="onItemClick"
- >
- <text class="iconfont icon-star"></text>
- </view>
- </view>
- </template>
- <script>
- import { getClientRect } from './common';
- export default {
- name: 'sx-rate',
- props: {
- // 当前值
- value: {
- type: [Number, String]
- },
- // 最大星星数量
- max: {
- type: Number,
- default: 5
- },
- // 禁用
- disabled: {
- type: Boolean,
- default: false
- },
- // 动画效果
- animation: {
- type: Boolean,
- default: true
- },
- // 默认星星颜色
- defaultColor: {
- type: String,
- default: '#ccc'
- },
- // 滑选后星星颜色
- activeColor: {
- type: String
- // default: '#FFB700'
- },
- // 星星大小
- fontSize: {
- type: String,
- default: 'inherit'
- },
- // 星星间距
- margin: {
- type: String,
- default: ''
- },
- // 自定义类名-容器
- containerClass: {
- type: String,
- default: ''
- },
- // 自定义类名-星星
- rateClass: {
- type: String,
- default: ''
- },
- index: {
- // 如果页面中存在多个该组件,通过该属性区分
- type: [Number, String]
- }
- },
- data() {
- return {
- rateValue: 0,
- touchMoving: false,
- startX: [],
- startW: 30
- };
- },
- computed: {
- list() {
- return [...new Array(this.max)].map((_, i) => i + 1);
- },
- rateMargin() {
- let margin = this.margin;
- if (!margin) return 0;
- switch (typeof margin) {
- case 'number':
- margin += 'px';
- case 'string':
- break;
- default:
- return 0;
- }
- let reg = /^(\d+)([^\d]*)/;
- let result = reg.exec(margin);
- if (!result) return 0;
- let [_, num, unit] = result;
- return num / 2 + unit;
- }
- },
- watch: {
- value: {
- handler(val) {
- this.rateValue = val;
- },
- immediate: true
- }
- },
- methods: {
- // 计算星星位置
- async initStartX() {
- let { max } = this;
- this.startX = [];
- for (let i = 0; i < max; i++) {
- let selector = `.rate-${i}`;
- let { left, width } = await getClientRect(selector, this);
- this.startX.push(left);
- this.startW = width;
- }
- },
- /**
- * 手指滑动事件回调
- * https://github.com/sunxi1997/uni-app-sx-rate/pull/1
- * 原本的触摸处理在自定了样式后可能会出现bug, 已解决
- */
- async ontouchmove(e) {
- if (!this.touchMoving) {
- this.touchMoving = true;
- // 开始手指滑动时重新计算星星位置,防止星星位置意外变化
- await this.initStartX();
- }
- let { startX, startW, max } = this;
- let { touches } = e;
- // 触摸焦点停留的位置
- let { pageX } = touches[touches.length - 1];
- // 超出最左边, 0 星
- if (pageX <= startX[0]) return this.toggle(0);
- // 刚好在第一颗星
- else if (pageX <= startX[0] + startW) return this.toggle(1);
- // 超出最右边, 最大星
- else if (pageX >= startX[max - 1]) return this.toggle(max);
- //计算星星停留的位置
- let startXHash = startX.concat(pageX).sort((a, b) => a - b);
- this.toggle(startXHash.indexOf(pageX));
- },
- // 点击回调
- onItemClick(e) {
- let { val } = e.currentTarget.dataset;
- this.toggle(val);
- },
- // 修改值
- toggle(val) {
- let { disabled } = this;
- if (disabled) return;
- if (this.rateValue !== val) {
- this.rateValue = val;
- this.$emit('update:value', val);
- let data = {
- index: this.index,
- value: val
- };
- this.$emit('change', data);
- }
- }
- },
- mounted() {}
- };
- </script>
- <style scoped>
- @import './sx-rate/iconfont.css';
- </style>
- <style lang="scss">
- .rate-box {
- min-height: 1.4em;
- display: flex;
- align-items: center;
- }
- .rate {
- display: inline-flex;
- justify-content: center;
- align-items: center;
- width: 1.2em;
- transition: all 0.15s linear;
- }
- .rate.scale {
- transform: scale(1.1);
- }
- .defaultColor {
- color: #ccc !important;
- }
- .activeColor {
- }
- </style>
|