Skip to content
This repository has been archived by the owner on Oct 27, 2021. It is now read-only.

Commit

Permalink
feat: add Range
Browse files Browse the repository at this point in the history
  • Loading branch information
pengshanglong committed May 14, 2020
1 parent 9c587db commit f042d91
Show file tree
Hide file tree
Showing 5 changed files with 310 additions and 1 deletion.
2 changes: 2 additions & 0 deletions src/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import AtRate from './rate/index'
import AtTextarea from './textarea/index'
import AtSearchBar from './search-bar/index'
import AtImagePicker from './image-picker/index'
import AtRange from './range/index'

export {
Grid,
Expand All @@ -48,4 +49,5 @@ export {
AtTextarea,
AtSearchBar,
AtImagePicker,
AtRange,
}
211 changes: 211 additions & 0 deletions src/components/range/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
import Vue, { VNode } from 'vue'
import classNames from 'classnames'
import { CommonEvent, ITouchEvent } from '@tarojs/components/types/common'
import { delayQuerySelector, getEventDetail } from '../../utils/common'
import mixins from '../mixins'

const AtRange = Vue.extend({
name: 'AtRange',
mixins: [mixins],
props: {
customStyle: {
type: [Object, String],
default: () => {},
},
className: {
type: [Object, String],
default: () => {},
},
sliderStyle: {
type: String,
default: '',
},
railStyle: {
type: String,
default: '',
},
trackStyle: {
type: String,
default: '',
},
value: {
type: Array,
default: () => [0, 0],
},
blockSize: {
type: Number,
default: 0,
},
min: {
type: Number,
default: 0,
},
max: {
type: Number,
default: 100,
},
disabled: {
type: Boolean,
default: false,
},
onChange: {
type: Function,
default: () => () => {},
},
onAfterChange: {
type: Function,
default: () => () => {},
},
},
data() {
const { min = 0, max = 100 } = this
return {
width: 0,
left: 0,
deltaValue: max - min,
currentSlider: '',
state: {
aX: 0,
bX: 0,
},
}
},
watch: {
value(v) {
this.updatePos()
if (this.value[0] !== v[0] || this.value[1] !== v[1]) {
this.setValue(v)
}
},
},
mounted() {
const { value } = this
this.updatePos()
this.setValue(value)
},

methods: {
handleClick(event: CommonEvent): void {
if (this.currentSlider && !this.disabled) {
let sliderValue = 0
const detail = getEventDetail(event)
sliderValue = detail.x - this.left
this.setSliderValue(this.currentSlider, sliderValue, 'onChange')
}
},

handleTouchMove(sliderName: string, event: ITouchEvent): void {
if (this.disabled) return
event.stopPropagation()

const clientX = event.touches[0].clientX
this.setSliderValue(sliderName, clientX - this.left, 'onChange')
},

handleTouchEnd(sliderName: string): void {
if (this.disabled) return

this.currentSlider = sliderName
this.triggerEvent('onAfterChange')
},

setSliderValue(sliderName: string, targetValue: number, funcName: string): void {
const distance = Math.min(Math.max(targetValue, 0), this.width)
const sliderValue = Math.floor((distance / this.width) * 100)
if (funcName) {
this.setState(
{
[sliderName]: sliderValue,
},
() => this.triggerEvent(funcName)
)
} else {
this.setState({
[sliderName]: sliderValue,
})
}
},

setValue(value: number[]): void {
const aX = Math.round(((value[0] - this.min) / this.deltaValue) * 100) // fix issue #670
const bX = Math.round(((value[1] - this.min) / this.deltaValue) * 100) // fix issue #670
this.setState({ aX, bX })
},

triggerEvent(funcName: string): void {
const { aX, bX } = this.state
const a = Math.round((aX / 100) * this.deltaValue) + this.min
const b = Math.round((bX / 100) * this.deltaValue) + this.min
const result = [a, b].sort((x, y) => x - y)

if (funcName === 'onChange') {
this.onChange && this.onChange(result)
} else if (funcName === 'onAfterChange') {
this.onAfterChange && this.onAfterChange(result)
}
},

updatePos(): void {
delayQuerySelector(this, '.at-range__container', 30).then((rect) => {
this.width = Math.round(rect[0].width)
this.left = Math.round(rect[0].left)
})
},
},
render(): VNode {
const { className, customStyle, sliderStyle, railStyle, trackStyle, blockSize, disabled } = this

const rootCls = classNames(
'at-range',
{
'at-range--disabled': disabled,
},
className
)

const { aX, bX } = this.state
const sliderCommonStyle = {
width: blockSize ? `${blockSize}PX` : '',
height: blockSize ? `${blockSize}PX` : '',
marginLeft: blockSize ? `${-blockSize / 2}PX` : '',
}
const sliderAStyle = {
...sliderCommonStyle,
left: `${aX}%`,
}
const sliderBStyle = {
...sliderCommonStyle,
left: `${bX}%`,
}
const containerStyle = {
height: blockSize ? `${blockSize}PX` : '',
}
const smallerX = Math.min(aX, bX)
const deltaX = Math.abs(aX - bX)
const atTrackStyle = {
left: `${smallerX}%`,
width: `${deltaX}%`,
}

return (
<view class={rootCls} style={customStyle} onClick={this.handleClick}>
<view class="at-range__container" style={containerStyle}>
<view class="at-range__rail" style={railStyle}></view>
<view class="at-range__track" style={this.$mergeStyle(atTrackStyle, trackStyle)}></view>
<view
class="at-range__slider"
style={this.$mergeStyle(sliderAStyle, sliderStyle)}
onTouchMove={this.handleTouchMove.bind(this, 'aX')}
onTouchEnd={this.handleTouchEnd.bind(this, 'aX')}></view>
<view
class="at-range__slider"
style={this.$mergeStyle(sliderBStyle, sliderStyle)}
onTouchMove={this.handleTouchMove.bind(this, 'bX')}
onTouchEnd={this.handleTouchEnd.bind(this, 'bX')}></view>
</view>
</view>
)
},
})

export default AtRange
6 changes: 6 additions & 0 deletions src/pages/index/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@
<view
class="index"
>
<AtRange
:value="rangeVal"
:min="0"
:max="100"
:on-change="rangeChange"
/>
<AtImagePicker
:files="imageFiles"
:on-change="imageChange"
Expand Down
6 changes: 6 additions & 0 deletions src/pages/index/indexMixins.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ import {
AtTextarea,
AtSearchBar,
AtImagePicker,
AtRange,
} from '../../components/index'

export default {
Expand Down Expand Up @@ -99,6 +100,7 @@ export default {
AtTextarea,
AtSearchBar,
AtImagePicker,
AtRange,
},
data() {
return {
Expand Down Expand Up @@ -237,6 +239,7 @@ export default {
type: 'btn',
},
],
rangeVal: [20, 60],
}
},
methods: {
Expand Down Expand Up @@ -291,5 +294,8 @@ export default {
imageChange(val) {
this.imageFiles = val
},
rangeChange(val) {
this.rangeVal = val
},
},
}
86 changes: 85 additions & 1 deletion src/utils/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ function handleTouchScroll(flag: any): void {
// 把脱离文档流的body拉上去!否则页面会回到顶部!
document.body.style.top = `${-scrollTop}px`
} else {
document.body.style.top = null
document.body.style.top = ''
document.body.classList.remove('at-frozen')

document.documentElement.scrollTop = scrollTop
Expand All @@ -137,6 +137,89 @@ function pxTransform(size: number): string {
return Taro.pxTransform(size)
}

interface EventDetail {
pageX: number;
pageY: number;
clientX: number;
clientY: number;
offsetX: number;
offsetY: number;
x: number;
y: number;
}

function getEventDetail(event: any): EventDetail {
let detail: EventDetail
switch (ENV) {
case Taro.ENV_TYPE.WEB:
detail = {
pageX: event.pageX,
pageY: event.pageY,
clientX: event.clientX,
clientY: event.clientY,
offsetX: event.offsetX,
offsetY: event.offsetY,
x: event.x,
y: event.y,
}
break

case Taro.ENV_TYPE.WEAPP:
detail = {
pageX: event.touches[0].pageX,
pageY: event.touches[0].pageY,
clientX: event.touches[0].clientX,
clientY: event.touches[0].clientY,
offsetX: event.target.offsetLeft,
offsetY: event.target.offsetTop,
x: event.target.x,
y: event.target.y,
}
break

case Taro.ENV_TYPE.ALIPAY:
detail = {
pageX: event.target.pageX,
pageY: event.target.pageY,
clientX: event.target.clientX,
clientY: event.target.clientY,
offsetX: event.target.offsetLeft,
offsetY: event.target.offsetTop,
x: event.target.x,
y: event.target.y,
}
break

case Taro.ENV_TYPE.SWAN:
detail = {
pageX: event.changedTouches[0].pageX,
pageY: event.changedTouches[0].pageY,
clientX: event.target.clientX,
clientY: event.target.clientY,
offsetX: event.target.offsetLeft,
offsetY: event.target.offsetTop,
x: event.detail.x,
y: event.detail.y,
}
break

default:
detail = {
pageX: 0,
pageY: 0,
clientX: 0,
clientY: 0,
offsetX: 0,
offsetY: 0,
x: 0,
y: 0,
}
console.warn('getEventDetail暂未支持该环境')
break
}
return detail
}

export {
getEnvs,
delayGetScrollOffset,
Expand All @@ -146,4 +229,5 @@ export {
isTest,
handleTouchScroll,
pxTransform,
getEventDetail,
}

0 comments on commit f042d91

Please sign in to comment.