React Native 雙滑塊滑動條

import React, { Component } from 'react';
import {
    View,
    PanResponder,
    StyleSheet,
    Text,
    Dimensions,
    Animated,
    Easing
} from 'react-native';

/* 數(shù)據(jù)相關(guān): 
 * 
 */
// const labels = ['0', '100', '200', '300', '400', '600', '1000', '不限'];
const labels = ['0', '200', '400', '600', '1000', '不限'];

/* 尺寸轉(zhuǎn)換方法
 * _px: 以750位基準(zhǔn)的轉(zhuǎn)屏幕
 * _pos: 以屏幕為基準(zhǔn)的轉(zhuǎn)750
 */
const dw = Dimensions.get('window').width;
const ratioDeps750 = dw / 750;
const _px = function getRpx(value) {
    return Math.floor(value * ratioDeps750);
}
const _pos = function getPos(value) {
    return Math.floor(value / ratioDeps750)
}

/* 尺寸相關(guān) 
 * excludeWidth: 距離屏幕左右邊界的距離,不觸發(fā)點擊滑動等事件的區(qū)域
 * unitWidth: 每一個單位的總寬度,有效區(qū)域等分后的寬度
 * labelWidth: 每一個單位的有效的點擊寬度
 * sliderWidth: 滑塊的寬度
 * sliderInitialLeft: 第一個滑塊的left
 */
const excludeWidth = 75;
const unitWidth = (750 - excludeWidth * 2) / labels.length; //100
const labelWidth = unitWidth - 20; // 兩個標(biāo)簽核心之間間隙 40
const sliderWidth = 60;
const halfUnitWidth = unitWidth / 2; // slider:halfUnitWidth - sliderWidth / 2; line:halfUnitWidth


const duration = 300;
const easing = Easing.linear;

export default class App extends Component {
    constructor(props) {
        super(props)
        let leftBoundaryForLeft = 0;
        let rightBoundaryForLeft = 0;
        let validPosForLeft = 0;
        let leftBoundaryForRight = 0;
        let rightBoundaryForRight = 0;
        let validPosForRight = 0;
        this.state = {
            leftIndex: 0,
            rightIndex: 0,
            leftPos: new Animated.Value(0),
            rightPos: new Animated.Value(0)
        }

        this._leftSliderPanResponder = PanResponder.create({
            onStartShouldSetPanResponder: (evt, gestureState) => true,
            onStartShouldSetPanResponderCapture: (evt, gestureState) => true,
            onMoveShouldSetPanResponder: (evt, gestureState) => true,
            onMoveShouldSetPanResponderCapture: (evt, gestureState) => true,
            onPanResponderTerminationRequest: (evt, gestureState) => true,
            onShouldBlockNativeResponder: (evt, gestureState) => true,
            onPanResponderGrant: (evt, gestureState) => {
                // 左邊界
                leftBoundaryForLeft = excludeWidth + halfUnitWidth;
                // 右邊界
                rightBoundaryForLeft = this.state.rightIndex * unitWidth + excludeWidth + halfUnitWidth;
                validPosForLeft = _pos(gestureState.x0)
            },
            onPanResponderMove: (evt, gestureState) => {
                //手勢中心點
                let centerX = _pos(gestureState.moveX)
                if (centerX >= leftBoundaryForLeft && centerX <= rightBoundaryForLeft - unitWidth) {
                    this.state.leftPos.setValue(centerX - excludeWidth);
                    validPosForLeft = centerX;
                    let posIndex = Math.floor((validPosForLeft - excludeWidth) / unitWidth);
                    this.setState({
                        leftIndex: posIndex
                    })
                }
            },
            onPanResponderRelease: (evt, gestureState) => {
                let posIndex = Math.floor((validPosForLeft - excludeWidth) / unitWidth);
                this.setPos({ leftIndex: posIndex })
            },
            onPanResponderTerminate: (evt, gestureState) => {

            },

        });
        this._rightSliderPanResponder = PanResponder.create({
            onStartShouldSetPanResponder: (evt, gestureState) => true,
            onStartShouldSetPanResponderCapture: (evt, gestureState) => true,
            onMoveShouldSetPanResponder: (evt, gestureState) => true,
            onMoveShouldSetPanResponderCapture: (evt, gestureState) => true,
            onPanResponderTerminationRequest: (evt, gestureState) => true,
            onShouldBlockNativeResponder: (evt, gestureState) => true,
            onPanResponderGrant: (evt, gestureState) => {
                // 左邊界
                leftBoundaryForRight = this.state.leftIndex * unitWidth + excludeWidth + halfUnitWidth;
                // 右邊界
                rightBoundaryForRight = 750 - excludeWidth - halfUnitWidth;
                validPosForRight = _pos(gestureState.x0)
            },
            onPanResponderMove: (evt, gestureState) => {
                //手勢中心點
                let centerX = _pos(gestureState.moveX)
                if (centerX >= leftBoundaryForRight + unitWidth && centerX <= rightBoundaryForRight) {
                    this.state.rightPos.setValue(centerX - excludeWidth);
                    validPosForRight = centerX;
                    let posIndex = Math.floor((validPosForRight - excludeWidth) / unitWidth);
                    this.setState({
                        rightIndex: posIndex
                    })
                }
            },
            onPanResponderRelease: (evt, gestureState) => {
                let posIndex = Math.floor((validPosForRight - excludeWidth) / unitWidth);
                this.setPos({ rightIndex: posIndex })
            },
            onPanResponderTerminate: (evt, gestureState) => {

            },
        });
        this._viewPanResponder = PanResponder.create({
            onStartShouldSetPanResponder: (evt, gestureState) => true,
            onStartShouldSetPanResponderCapture: (evt, gestureState) => true,
            onMoveShouldSetPanResponder: (evt, gestureState) => true,
            onMoveShouldSetPanResponderCapture: (evt, gestureState) => true,
            onPanResponderTerminationRequest: (evt, gestureState) => true,
            onShouldBlockNativeResponder: (evt, gestureState) => true,
            onPanResponderGrant: (evt, gestureState) => {

            },
            onPanResponderMove: (evt, gestureState) => {

            },
            onPanResponderRelease: (evt, gestureState) => {
                if (gestureState.moveX) {
                    return;
                }
                let x = _pos(gestureState.x0);
                if ((x - excludeWidth) % unitWidth < (unitWidth - labelWidth) / 2 || (x - excludeWidth) % unitWidth > unitWidth - (unitWidth - labelWidth) / 2) {
                    console.log('無效點')
                    return;
                }
                let posIndex = Math.floor((x - excludeWidth) / unitWidth);

                let { leftIndex, rightIndex } = this.state;
                let leftPos = leftIndex * unitWidth + halfUnitWidth;
                let rightPos = rightIndex * unitWidth + halfUnitWidth;

                // 距離左邊的點更近
                if (Math.abs(x - excludeWidth - leftPos) <= Math.abs(x - excludeWidth - rightPos)) {
                    this.setPos({ leftIndex: posIndex === rightIndex ? posIndex - 1 : posIndex })
                } else {
                    this.setPos({ rightIndex: posIndex === leftIndex ? posIndex + 1 : posIndex })
                }
            },
            onPanResponderTerminate: (evt, gestureState) => {

            },

        });
    }

    render() {
        let { leftPos, rightPos, leftIndex, rightIndex } = this.state;
        return (
            <View style={styles.container}>
                <View style={styles.content}>
                    <View {...this._viewPanResponder.panHandlers} style={styles.pan} >
                        <View style={styles.labels}>
                            {labels.map((item, index) => {
                                return <Text key={index} style={{ ...StyleSheet.flatten(styles.label), color: index < leftIndex || index > rightIndex ? '#898989' : '#3F70C1' }}>{item}</Text>
                            })}
                        </View>
                        <View style={styles.lines}>
                            <View style={styles.line} />
                            <Animated.View style={{
                                ...StyleSheet.flatten(styles.hl_line),
                                left: leftPos.interpolate({
                                    inputRange: [halfUnitWidth, labels.length * unitWidth - halfUnitWidth],
                                    outputRange: [_px(halfUnitWidth), _px(labels.length * unitWidth - halfUnitWidth)]//
                                }),
                                right: rightPos.interpolate({
                                    inputRange: [halfUnitWidth, labels.length * unitWidth - halfUnitWidth],
                                    outputRange: [_px(labels.length * unitWidth - halfUnitWidth), _px(halfUnitWidth)]//
                                })
                            }} />
                        </View>
                    </View>
                    <Animated.Image {...this._leftSliderPanResponder.panHandlers} source={images.slider} style={{
                        ...StyleSheet.flatten(styles.slider),
                        left: leftPos.interpolate({
                            inputRange: [halfUnitWidth, labels.length * unitWidth - halfUnitWidth],
                            outputRange: [_px(halfUnitWidth), _px(labels.length * unitWidth - halfUnitWidth)]//
                        }),
                    }} />
                    <Animated.Image {...this._rightSliderPanResponder.panHandlers} source={images.slider} style={{
                        ...StyleSheet.flatten(styles.slider),
                        left: rightPos.interpolate({
                            inputRange: [halfUnitWidth, labels.length * unitWidth - halfUnitWidth],
                            outputRange: [_px(halfUnitWidth), _px(labels.length * unitWidth - halfUnitWidth)]//
                        }),
                    }} />
                </View>
                <Text>{leftIndex}</Text>
                <Text>{rightIndex}</Text>
            </View>
        );
    }
    componentDidMount() {
        this.setPos({ leftIndex: 0, rightIndex: labels.length - 1 })
    }
    setPos({ leftIndex, rightIndex }) {
        if (leftIndex >= 0) {
            Animated.parallel([
                Animated.timing(this.state.leftPos, {
                    toValue: leftIndex * unitWidth + halfUnitWidth,
                    duration,
                    easing,
                })
            ]).start(() => {
                this.setState({
                    leftIndex: leftIndex
                })
            })
        }
        if (rightIndex >= 0) {
            Animated.parallel([
                Animated.timing(this.state.rightPos, {
                    toValue: rightIndex * unitWidth + halfUnitWidth,
                    duration,
                    easing,
                }),
            ]).start(() => {
                this.setState({
                    rightIndex: rightIndex
                })
            })
        }
    }
}

const styles = StyleSheet.create({
    container: {
        height: _px(180),
        backgroundColor: '#fff'
    },
    content: {
        backgroundColor: '#fff',
        height: _px(180),
        position: 'relative',
        marginHorizontal: _px(excludeWidth),
        marginVertical: _px(30),
        height: _px(120),
    },
    pan: {
        backgroundColor: '#fff'
    },
    labels: {
        height: _px(60),
        flexDirection: 'row',
        justifyContent: 'space-around',
        alignItems: 'center'
    },
    label: {
        width: _px(labelWidth),
        textAlign: 'center',
    },
    lines: {
        height: _px(60),
        justifyContent: 'center',
    },
    line: {
        position: 'absolute',
        backgroundColor: '#ddd',
        height: _px(4),
        left: _px(halfUnitWidth),
        right: _px(halfUnitWidth)
    },
    hl_line: {
        position: 'absolute',
        backgroundColor: '#3F70C1',
        height: _px(4),
    },
    slider: {
        width: _px(sliderWidth),
        height: _px(sliderWidth),
        resizeMode: 'contain',
        position: 'absolute',
        top: _px(90),
        transform: [{
            translateX: -_px(sliderWidth / 2),
        }, {
            translateY: -_px(sliderWidth / 2),
        }]
    }
});
const images = {
    slider: require('./img/slider.png')
}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末物舒,一起剝皮案震驚了整個濱河市乞娄,隨后出現(xiàn)的幾起案子火架,更是在濱河造成了極大的恐慌缎患,老刑警劉巖惋增,帶你破解...
    沈念sama閱讀 218,755評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件奸忽,死亡現(xiàn)場離奇詭異堕伪,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)栗菜,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評論 3 395
  • 文/潘曉璐 我一進(jìn)店門欠雌,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人疙筹,你說我怎么就攤上這事富俄。” “怎么了而咆?”我有些...
    開封第一講書人閱讀 165,138評論 0 355
  • 文/不壞的土叔 我叫張陵霍比,是天一觀的道長。 經(jīng)常有香客問我暴备,道長悠瞬,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,791評論 1 295
  • 正文 為了忘掉前任馍驯,我火速辦了婚禮暇唾,結(jié)果婚禮上唯鸭,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好羹奉,可當(dāng)我...
    茶點故事閱讀 67,794評論 6 392
  • 文/花漫 我一把揭開白布浆兰。 她就那樣靜靜地躺著雨饺,像睡著了一般荐吉。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上蝗拿,一...
    開封第一講書人閱讀 51,631評論 1 305
  • 那天晾捏,我揣著相機(jī)與錄音,去河邊找鬼哀托。 笑死惦辛,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的仓手。 我是一名探鬼主播胖齐,決...
    沈念sama閱讀 40,362評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼玻淑,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了呀伙?” 一聲冷哼從身側(cè)響起补履,我...
    開封第一講書人閱讀 39,264評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎剿另,沒想到半個月后箫锤,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,724評論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡雨女,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年谚攒,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片戚篙。...
    茶點故事閱讀 40,040評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡五鲫,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出岔擂,到底是詐尸還是另有隱情,我是刑警寧澤浪耘,帶...
    沈念sama閱讀 35,742評論 5 346
  • 正文 年R本政府宣布乱灵,位于F島的核電站,受9級特大地震影響七冲,放射性物質(zhì)發(fā)生泄漏痛倚。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,364評論 3 330
  • 文/蒙蒙 一澜躺、第九天 我趴在偏房一處隱蔽的房頂上張望蝉稳。 院中可真熱鬧,春花似錦掘鄙、人聲如沸耘戚。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽收津。三九已至,卻和暖如春浊伙,著一層夾襖步出監(jiān)牢的瞬間撞秋,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評論 1 270
  • 我被黑心中介騙來泰國打工嚣鄙, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留吻贿,地道東北人。 一個月前我還...
    沈念sama閱讀 48,247評論 3 371
  • 正文 我出身青樓哑子,卻偏偏與公主長得像舅列,于是被迫代替她去往敵國和親肌割。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,979評論 2 355

推薦閱讀更多精彩內(nèi)容