123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229 |
- <!--
- sx-rate uni-app滑选星星组件
- 版本 1.1.2
- 日期 2020-6-4
- 作者 sunxi1997
- 链接 https://ext.dcloud.net.cn/plugin?id=1027
- -->
- <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;
- }
- },
- /**
- * 手指滑动事件回调
- * 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;
- 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>
|