需求
有一个图片上传的功能,选择完图片之后会按照选择顺序进行排序,系统会默认前5张为系统展示图片,其他的图片留存在系统内供运营商使用. 图片选择完之后可以进行拖拽,调整顺序.
先上效果图
GIF.gif1. 响应手势事件,绑定相关的方法.
componentWillMount() {
this._panResponder = PanResponder.create({
//用户开始触摸屏幕的时候,是否愿意成为响应者;
onStartShouldSetPanResponder: () => true,
//用户开始触摸屏幕的时候,是否愿意成为响应者;
onMoveShouldSetPanResponder: () => true,
// 开始按下的时候 调用的方法.
onPanResponderGrant: (evt, gestureState) => this.onPanResponderGrant(evt),
// 手指移动的时候调用的方法
onPanResponderMove: (evt, gestureState) => this.onPanResponderMove(evt, gestureState),
// 手指放开的调用的方法.
onPanResponderRelease: (evt, gestureState) => this.onPanResponderEnd(),
onPanResponderTerminate: (evt, gestureState) => this.onPanResponderEnd()
});
}
render() {
const views1 = [];
const views2 = [];
this.names.forEach((value, index) => {
let bg = this.color[index];
if (index >= 5) {
views2.push(
<View
// 加了这句 View 才能响应手势
{...this._panResponder.panHandlers}
key={value}
style={
[styles.circle, {
left: (index - 5) * CIRCLE_SIZE,
backgroundColor: bg,
top: CIRCLE_SIZE,
}]}
ref={ref => this.names[index] = ref}
/>)
} else {
views1.push(
<View
// 加了这句 View 才能响应手势
{...this._panResponder.panHandlers}
key={value}
style={
[styles.circle,
{left: index * CIRCLE_SIZE},
{backgroundColor: bg},
]}
ref={ref => this.names[index] = ref}
/>)
}
});
return (
<View
style={styles.container}>
{views1}
{views2}
</View>
);
}
2 需要根据按下的位置来判断 要移动哪个view
// 根据按下的坐标,判断索引
getIdByPosition(pageX, pageY) {
let index = -1;
if (pageX > CIRCLE_SIZE * this.names.length) {
index = this.names.length - 1;
} else {
index = Math.floor((pageX) / CIRCLE_SIZE)
}
// 如果触摸的高度(需要减去导航栏的高度)大于圆的半径,说明是第二行
if (pageY - NAV_BAR_HEIGHT > CIRCLE_SIZE) {
index += 5;
}
return index;
}
static getLeftValueYById(id) {
return id >= 5 ? (id - 5) * CIRCLE_SIZE : id * CIRCLE_SIZE;
}
static getTopValueById(id) {
return id >= 5 ? CIRCLE_SIZE : 0;
}
3. 开始按下的操作
// 开始按下手势操作。加点偏移量给用户一些视觉反馈,让他们知道发生了什么事情!
onPanResponderGrant(evt) {
const {pageX, locationX, pageY, locationY} = evt.nativeEvent;
this.index = this.getIdByPosition(pageX, pageY);
console.log("this.index" + this.index);
this.preX = pageX - locationX;
this.preY = pageY - locationY;
// 给退拽的item加个阴影
let item = this.names[this.index];
item.setNativeProps({
style: {
shadowColor: "red",
shadowOpacity: 0.5,
shadowRadius: 5,
shadowOffset: {height: 10, width: 10},
elevation: 5,
top: -2,
}
});
}
3. 移动的操作, 移动的时候 需要根据手指移动的位置,来判断哪两个小球交换.
// 开始移动
onPanResponderMove(evt, gestureState) {
// 按下的小球
let index = this.index;
// 按下的小球需要移动到哪
let left = this.preX + gestureState.dx;
let top = this.preY + gestureState.dy;
// 根据ref 来获取索引对应的view,setNativeProps来设置小球的位置
let item = this.names[this.index];
item.setNativeProps({
style: {
left: left,
top: top,
}
});
// 手指移动的某个点对应的索引,如果手指移动到某个点对应的索引 与按下那个点对应的索引不同,就交换两个小球的位置.
let collideIndex = this.getIdByPosition(evt.nativeEvent.pageX, evt.nativeEvent.pageY);
if (collideIndex !== this.index && collideIndex !== -1) {
let collideItem = this.names[collideIndex];
collideItem.setNativeProps({
style: {
left: ListViewPage.getLeftValueYById(this.index),
top: ListViewPage.getTopValueById(this.index)
}
});
//交换两个值
[this.names[this.index], this.names[collideIndex]] = [this.names[collideIndex], this.names[this.index]];
[this.order[this.index], this.order[collideIndex]] = [this.order[collideIndex], this.order[this.index]];
this.index = collideIndex;
}
}
4. 手指释放的操作
onPanResponderEnd() {
// 复位
const shadowStyle = {
shadowColor: "#000",
shadowOpacity: 0,
shadowRadius: 0,
shadowOffset: {height: 0, width: 0,},
elevation: 0
};
// 这个索引是已经交换过的索引.
let item = this.names[this.index];
let index = this.index;
let leftValue = ListViewPage.getLeftValueYById(this.index);
let topValue = ListViewPage.getTopValueById(this.index);
item.setNativeProps({
style: {
...shadowStyle,
left: leftValue,
top: topValue,
}
});
}
5.全部的代码
import React, {Component} from 'react';
import {
StyleSheet,
View,
PanResponder,
Animated,
LayoutAnimation,
UIManager,
} from 'react-native';
/**
* 自定义拖拽图片排序
**/
const CIRCLE_SIZE = 70;
const NAV_BAR_HEIGHT = 0;
export default class DragSort extends Component {
constructor(props) {
super(props);
this.names = ['Android', 'iOS', 'js', 'jq', '00', '11', '22', '33', '44', '55'];
this.order = ['Android', 'iOS', 'js', 'jq', '00', '11', '22', '33', '44', '55'];
this.color = ['red', 'blue', 'black', 'pink', 'gray', '#03ef94', '#009688', '#607d8b', '#3f51b5', '#00796b'];
}
// 开始按下手势操作。加点偏移量给用户一些视觉反馈,让他们知道发生了什么事情!
onPanResponderGrant(evt) {
const {pageX, locationX, pageY, locationY} = evt.nativeEvent;
this.index = this.getIdByPosition(pageX, pageY);
console.log("this.index" + this.index);
this.preX = pageX - locationX;
this.preY = pageY - locationY;
// 给退拽的item加个阴影
let item = this.names[this.index];
item.setNativeProps({
style: {
shadowColor: "red",
shadowOpacity: 0.5,
shadowRadius: 5,
shadowOffset: {height: 10, width: 10},
elevation: 5,
top: -2,
}
});
}
// 最近一次的移动距离为gestureState.move{X,Y}
onPanResponderMove(evt, gestureState) {
let index = this.index;
let left = this.preX + gestureState.dx;
let top = this.preY + gestureState.dy;
let item = this.names[this.index];
item.setNativeProps({
style: {
left: left,
top: top,
}
});
let collideIndex = this.getIdByPosition(evt.nativeEvent.pageX, evt.nativeEvent.pageY);
if (collideIndex !== this.index && collideIndex !== -1) {
let collideItem = this.names[collideIndex];
collideItem.setNativeProps({
style: {
left: ListViewPage.getLeftValueYById(this.index),
top: ListViewPage.getTopValueById(this.index)
}
});
//交换两个值
[this.names[this.index], this.names[collideIndex]] = [this.names[collideIndex], this.names[this.index]];
[this.order[this.index], this.order[collideIndex]] = [this.order[collideIndex], this.order[this.index]];
this.index = collideIndex;
}
}
// 用户放开了所有的触摸点,且此时视图已经成为了响应者。
// 一般来说这意味着一个手势操作已经成功完成。
onPanResponderEnd() {
// 复位
const shadowStyle = {
shadowColor: "#000",
shadowOpacity: 0,
shadowRadius: 0,
shadowOffset: {height: 0, width: 0,},
elevation: 0
};
let item = this.names[this.index];
let index = this.index;
let leftValue = ListViewPage.getLeftValueYById(this.index);
let topValue = ListViewPage.getTopValueById(this.index);
item.setNativeProps({
style: {
...shadowStyle,
left: leftValue,
top: topValue,
}
});
console.log(this.order);
}
// 根据按下的坐标,判断索引
getIdByPosition(pageX, pageY) {
let index = -1;
if (pageX > CIRCLE_SIZE * this.names.length) {
index = this.names.length - 1;
} else {
index = Math.floor((pageX) / CIRCLE_SIZE)
}
// 如果触摸的高度(需要减去导航栏的高度)大于圆的半径,说明是第二行
if (pageY - NAV_BAR_HEIGHT > CIRCLE_SIZE) {
index += 5;
}
return index;
}
static getLeftValueYById(id) {
return id >= 5 ? (id - 5) * CIRCLE_SIZE : id * CIRCLE_SIZE;
}
static getTopValueById(id) {
return id >= 5 ? CIRCLE_SIZE : 0;
}
componentWillMount() {
this._panResponder = PanResponder.create({
//用户开始触摸屏幕的时候,是否愿意成为响应者;
onStartShouldSetPanResponder: () => true,
//用户开始触摸屏幕的时候,是否愿意成为响应者;
onMoveShouldSetPanResponder: () => true,
// 开始触摸的时候 调用的方法.
onPanResponderGrant: (evt, gestureState) => this.onPanResponderGrant(evt),
// 手指移动的时候调用的方法
onPanResponderMove: (evt, gestureState) => this.onPanResponderMove(evt, gestureState),
// 手指放开的调用的方法.
onPanResponderRelease: (evt, gestureState) => this.onPanResponderEnd(),
onPanResponderTerminate: (evt, gestureState) => this.onPanResponderEnd()
});
}
render() {
const views1 = [];
const views2 = [];
this.names.forEach((value, index) => {
let bg = this.color[index];
if (index >= 5) {
views2.push(
<View
{...this._panResponder.panHandlers}
key={value}
style={
[styles.circle, {
left: (index - 5) * CIRCLE_SIZE,
backgroundColor: bg,
top: CIRCLE_SIZE,
}]}
ref={ref => this.names[index] = ref}
/>)
} else {
views1.push(
<View
{...this._panResponder.panHandlers}
key={value}
style={
[styles.circle,
{left: index * CIRCLE_SIZE},
{backgroundColor: bg},
]}
ref={ref => this.names[index] = ref}
/>)
}
});
return (
<View
style={styles.container}>
{views1}
{views2}
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1
},
circle: {
width: CIRCLE_SIZE,
height: CIRCLE_SIZE,
borderRadius: CIRCLE_SIZE / 2,
backgroundColor: 'blue',
position: 'absolute',
}
});
网友评论