123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229 |
- <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, color: val<=rateValue ? activeColor : defaultColor}"
- :class="[{scale: !disabled && val<=rateValue && animation && touchMoving},`rate-${i}`,rateClass]"
- :data-val="val"
- @click="onItemClick"
- >
- <text class="iconfont icon-star" />
- </view>
- </view>
- </template>
- <script>
- import { getClientRect } from "./common";
- export default {
- name: 'sx-rate',
- props: {
-
- value: {
- type: Number,
- default: 3
- },
-
- 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: ''
- }
- },
- 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;
- }
- },
-
- 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];
-
- 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;
- val = +val;
- if (disabled || isNaN(val))
- return;
- if (this.rateValue !== val) {
- this.rateValue = val;
- this.$emit('update:value', val);
- this.$emit('change', val)
- }
- }
- },
- }
- </script>
- <style scoped>
- @import "../../static/sx-rate/iconfont.css";
- </style>
- <style scoped>
- .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 .15s linear;
- }
- .rate.scale {
- transform: scale(1.1);
- }
- </style>
|