gpt4 book ai didi

react-native - 拖放和交换项目动画?

转载 作者:行者123 更新时间:2023-12-03 14:44:29 24 4
gpt4 key购买 nike

我似乎让拖放部分正常工作,但不知道如何进行交换位置。也不知道如何解决 z-index 问题(它似乎对 Animated.View 做了一些可疑的事情)。

enter image description here

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

class Draggable extends Component {
constructor(props) {
super(props);

this.state = {
pan: new Animated.ValueXY(),
scale: new Animated.Value(1),
};
}

componentWillMount() {
this._panResponder = PanResponder.create({
onMoveShouldSetResponderCapture: () => true,
onMoveShouldSetPanResponderCapture: () => true,

onPanResponderGrant: (e, gestureState) => {
this.state.pan.setOffset({x: this.state.pan.x._value, y: this.state.pan.y._value});
this.state.pan.setValue({x: 0, y: 0});
Animated.spring(
this.state.scale,
{ toValue: 1.1, friction: 3 }
).start();
},

onPanResponderMove: Animated.event([
null, {dx: this.state.pan.x, dy: this.state.pan.y},
]),

onPanResponderRelease: (e, gesture) => {
this.state.pan.flattenOffset();
Animated.spring(
this.state.scale,
{ toValue: 1, friction: 3 }
).start();

let dropzone = this.inDropZone(gesture);

if (dropzone) {
console.log(dropzone.y-this.layout.y, this.state.pan.y._value, dropzone.y);
Animated.spring(
this.state.pan,
{toValue:{
x: 0,
y: dropzone.y-this.layout.y,
}}
).start();
} else {
Animated.spring(
this.state.pan,
{toValue:{x:0,y:0}}
).start();
}
},
});
}

inDropZone(gesture) {
var isDropZone = false;
for (dropzone of this.props.dropZoneValues) {
if (gesture.moveY > dropzone.y && gesture.moveY < dropzone.y + dropzone.height && gesture.moveX > dropzone.x && gesture.moveX < dropzone.x + dropzone.width) {
isDropZone = dropzone;
}
}
return isDropZone;
}

setDropZoneValues(event) {
this.props.setDropZoneValues(event.nativeEvent.layout);
this.layout = event.nativeEvent.layout;
}

render() {
let { pan, scale } = this.state;
let [translateX, translateY] = [pan.x, pan.y];
let rotate = '0deg';
let imageStyle = {transform: [{translateX}, {translateY}, {rotate}, {scale}]};

return (
<View
style={styles.dropzone}
onLayout={this.setDropZoneValues.bind(this)}
>
<Animated.View
style={[imageStyle, styles.draggable]}
{...this._panResponder.panHandlers}>
<Image style={styles.image} resizeMode="contain" source={{ uri: this.props.uri }} />
</Animated.View>
</View>
);
}
}


class Playground extends Component {
constructor(props) {
super(props);

this.state = {
dropZoneValues: [],
};
}

setDropZoneValues(layout) {
this.setState({
dropZoneValues: this.state.dropZoneValues.concat(layout),
});
}

render() {

return (
<View style={styles.container}>
<Draggable
dropZoneValues={this.state.dropZoneValues}
setDropZoneValues={this.setDropZoneValues.bind(this)}
uri="https://pbs.twimg.com/profile_images/378800000822867536/3f5a00acf72df93528b6bb7cd0a4fd0c.jpeg"
/>
<Draggable
dropZoneValues={this.state.dropZoneValues}
setDropZoneValues={this.setDropZoneValues.bind(this)}
uri="https://pbs.twimg.com/profile_images/446566229210181632/2IeTff-V.jpeg"
/>
</View>
);
}
}

const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: 'orange',
justifyContent: 'center',
alignItems: 'center',
},
dropzone: {
zIndex: 0,
margin: 5,
width: 106,
height: 106,
borderColor: 'green',
borderWidth: 3
},
draggable: {
zIndex: 0,
backgroundColor: 'white',
justifyContent: 'center',
alignItems: 'center',
width: 100,
height: 100,
borderWidth: 1,
borderColor: 'black'
},
image: {
width: 75,
height: 75
}
});

export default Playground;

编辑:我尝试了交换,但似乎只工作了大约一半的时间。此外,zIndex 仍然让我发疯。我正在打印状态,如 {color} {zIndex} ,所以你可以看到它更新到 100,但它似乎没有生效。将颜色更改为蓝色似乎有效……我很困惑。

enter image description here
import React, { Component } from 'react';
import {
StyleSheet,
Text,
View,
Image,
PanResponder,
Animated,
Alert,
} from 'react-native';

class Draggable extends Component {
constructor(props) {
super(props);

this.state = {
pan: new Animated.ValueXY(),
scale: new Animated.Value(1),
zIndex: 0,
color: 'white',
};
}

componentWillMount() {
this._panResponder = PanResponder.create({
onMoveShouldSetResponderCapture: () => true,
onMoveShouldSetPanResponderCapture: () => true,

onPanResponderGrant: (e, gestureState) => {
console.log('moving', this.props.index);
this.state.pan.setOffset({x: this.state.pan.x._value, y: this.state.pan.y._value});
this.state.pan.setValue({x: 0, y: 0});
Animated.spring(
this.state.scale,
{ toValue: 1.1, friction: 3 }
).start();

this.setState({ color: 'blue', zIndex: 100 });
},

onPanResponderMove: Animated.event([null,
{ dx: this.state.pan.x, dy: this.state.pan.y },
]),

onPanResponderRelease: (e, gesture) => {
this.state.pan.flattenOffset();
// de-scale
Animated.spring(
this.state.scale,
{ toValue: 1, friction: 3 }
).start();

this.setState({ color: 'white', zIndex: 0 });

let dropzone = this.inDropZone(gesture);
if (dropzone) { // plop into dropzone
// console.log(dropzone.y-this.layout.y, this.state.pan.y._value, dropzone.y);
console.log('grabbed', this.props.index, ' => dropped', dropzone.index);
Animated.spring(
this.state.pan,
{toValue:{
x: 0,
y: dropzone.y-this.layout.y,
}}
).start();
if (this.props.index !== dropzone.index) {
this.props.swapItems(this.props.index, dropzone.index, dropzone.y-this.layout.y);
}
} else {
// spring back to start
Animated.spring(
this.state.pan,
{toValue:{x:0,y:0}}
).start();
}
},
});
}

inDropZone(gesture) {
var isDropZone = false;
for (var dropzone of this.props.dropZoneValues) {
if (gesture.moveY > dropzone.y && gesture.moveY < dropzone.y + dropzone.height) {
isDropZone = dropzone;
}
}
return isDropZone;
}

setDropZoneValues(event) {
this.props.setDropZoneValues(event.nativeEvent.layout, this.props.index, this);
this.layout = event.nativeEvent.layout;
this.layout.index = this.props.index;
}

render() {
let { pan, scale, zIndex, color } = this.state;
let [translateX, translateY] = [pan.x, pan.y];
let rotate = '0deg';
let imageStyle = {
transform: [{translateX}, {translateY}, {rotate}, {scale}]
};

return (
<View
style={[styles.dropzone]}
onLayout={this.setDropZoneValues.bind(this)}
>
<Animated.View
{...this._panResponder.panHandlers}
style={[imageStyle, styles.draggable, { backgroundColor: color, zIndex }]}
>
<Text>{this.props.index}</Text>
<Text>{this.props.char}</Text>
<Text>{this.state.color} {this.state.zIndex}</Text>
</Animated.View>
</View>
);
}
}

Array.prototype.swap = function (x,y) {
var b = this[x];
this[x] = this[y];
this[y] = b;
return this;
}

Array.prototype.clone = function() {
return this.slice(0);
};

const items = [
'shiba inu',
'labrador',
];

class Playground extends Component {
constructor(props) {
super(props);

this.state = {
items,
dropZoneValues: [],
dropzones: [],
};
}

setDropZoneValues(layout, index, dropzone) {
layout.index = index;
this.setState({
dropZoneValues: this.state.dropZoneValues.concat(layout),
});
this.setState({
dropzones: this.state.dropzones.concat(dropzone),
});
}

swapItems(i1, i2, y) {
console.log('swapping', i1, i2);
var height = y < 0 ? this.state.dropzones[i1].layout.height : -this.state.dropzones[i1].layout.height;
Animated.spring(
this.state.dropzones[i2].state.pan,
{toValue:{
x: 0,
y: -y-height
}}
).start();
var clone = this.state.items.clone();
console.log(clone);
clone.swap(i1, i2);
console.log(clone);
this.setState({
items: clone
});
}

render() {
console.log('state', this.state);

return (
<View style={styles.container}>
{this.state.items.map((i, index) =>
<Draggable key={index}
dropZoneValues={this.state.dropZoneValues}
setDropZoneValues={this.setDropZoneValues.bind(this)}
char={i}
index={index}
swapItems={this.swapItems.bind(this)}
/>
)}
<View style={{ zIndex: 100, backgroundColor: 'red' }}><Text>foo</Text></View>
<View style={{ zIndex: -100, top: -10, backgroundColor: 'blue' }}><Text>bar</Text></View>
</View>
);
}
}

const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: 'orange',
justifyContent: 'center',
alignItems: 'center',
},
dropzone: {
// margin: 5,
zIndex: -100,
width: 106,
height: 106,
borderColor: 'green',
borderWidth: 3,
backgroundColor: 'lightgreen',
},
draggable: {
backgroundColor: 'white',
justifyContent: 'center',
alignItems: 'center',
width: 100,
height: 100,
borderWidth: 1,
borderColor: 'black'
},
image: {
width: 75,
height: 75
}
});

export default Playground;

编辑2: zIndex只影响 child 的 sibling ,所以我不得不把它放在 parent (绿色框)而不是 Animated.View 上。 .

交换只工作一半的原因是因为我在 addDropzone 中添加布局的方式,它们有时会在 inDropzone 中无法使用.当我对布局进行排序时, inDropzone像我期望的那样工作。

总的来说,这整个事情仍然感觉像是 巨型黑客 ,所以如果任何真正知道他们在做什么的人看到我的实现中的缺陷并且可以改进它,那就太好了。此外,有一个预览会很好,所以当你拖过一个放置区时,它会显示将要更改的内容的临时交换,或者你能想到的任何其他有用的视觉指示器。拖放和交换是移动应用程序非常常见的功能,唯一的库只能在垂直列表中使用。我需要从头开始实现它,因为我想让它变成一个照片网格。

enter image description here
import React, { Component } from 'react';
import {
StyleSheet,
Text,
View,
Image,
PanResponder,
Animated,
Alert,
} from 'react-native';
import _ from 'lodash';

class Draggable extends Component {
constructor(props) {
super(props);

this.state = {
pan: new Animated.ValueXY(),
scale: new Animated.Value(1),
zIndex: 0,
backgroundColor: 'white',
};
}

handleOnLayout(event) {
const { addDropzone } = this.props;
const { layout } = event.nativeEvent;
this.layout = layout;
addDropzone(this, layout);
}

componentWillMount() {
const { inDropzone, swapItems, index } = this.props;

this._panResponder = PanResponder.create({
onMoveShouldSetResponderCapture: () => true,
onMoveShouldSetPanResponderCapture: () => true,

onPanResponderGrant: (e, gestureState) => {
console.log('moving', index);
this.state.pan.setOffset({ x: this.state.pan.x._value, y: this.state.pan.y._value });
this.state.pan.setValue({ x: 0, y: 0 });

Animated.spring(this.state.scale, { toValue: 0.75, friction: 3 }).start();

this.setState({ backgroundColor: 'deepskyblue', zIndex: 1 });
},

onPanResponderMove: Animated.event([null, { dx: this.state.pan.x, dy: this.state.pan.y }]),

onPanResponderRelease: (e, gesture) => {
this.state.pan.flattenOffset();
Animated.spring(this.state.scale, { toValue: 1 }).start();
this.setState({ backgroundColor: 'white', zIndex: 0 });

let dropzone = inDropzone(gesture);
if (dropzone) {
console.log('in dropzone', dropzone.index);
// adjust into place
Animated.spring(this.state.pan, { toValue: {
x: dropzone.x - this.layout.x,
y: dropzone.y - this.layout.y,
} }).start();
if (index !== dropzone.index) {
swapItems(index, dropzone.index);
}
}
Animated.spring(this.state.pan, { toValue: { x: 0, y: 0 } }).start();
}

});
}

render() {
const { pan, scale, zIndex, backgroundColor } = this.state;
const [translateX, translateY] = [pan.x, pan.y];
const rotate = '0deg';
const imageStyle = {
transform: [{ translateX }, { translateY }, { rotate }, { scale }],
};

return (
<View
style={[styles.dropzone, { zIndex }]}
onLayout={event => this.handleOnLayout(event)}
>
<Animated.View
{...this._panResponder.panHandlers}
style={[imageStyle, styles.draggable, { backgroundColor }]}
>
<Image style={styles.image} source={{ uri: this.props.item }} />
</Animated.View>
</View>
);
}
}

const swap = (array, fromIndex, toIndex) => {
const newArray = array.slice(0);
newArray[fromIndex] = array[toIndex];
newArray[toIndex] = array[fromIndex];
return newArray;
}

class Playground extends Component {
constructor(props) {
super(props);

this.state = {
items: [
'https://files.graphiq.com/465/media/images/t2/Shiba_Inu_5187048.jpg',
'https://i.ytimg.com/vi/To8oesttqc4/hqdefault.jpg',
'https://vitaminsforpitbulls.com/wp-content/uploads/2013/06/english-bulldog-puppy-for-sale-909x1024.jpg',
'https://s-media-cache-ak0.pinimg.com/236x/20/16/e6/2016e61e8642c8aab60c71f6e3bcd004.jpg',
'https://pbs.twimg.com/profile_images/446566229210181632/2IeTff-V.jpeg',
'https://s-media-cache-ak0.pinimg.com/236x/fa/7b/18/fa7b185924d9d4d14a0623bc567f4e87.jpg',
],
dropzones: [],
dropzoneLayouts: [],
};
}

addDropzone(dropzone, dropzoneLayout) {
const { items, dropzones, dropzoneLayouts } = this.state;
// HACK: to make sure setting state does not re-add dropzones
if (items.length !== dropzones.length) {
this.setState({
dropzones: [...dropzones, dropzone],
dropzoneLayouts: [...dropzoneLayouts, dropzoneLayout],
});
}
}

inDropzone(gesture) {
const { dropzoneLayouts } = this.state;
// HACK: with the way they are added, sometimes the layouts end up out of order, so we need to sort by y,x (x,y doesn't work)
const sortedDropzoneLayouts = _.sortBy(dropzoneLayouts, ['y', 'x']);
let inDropzone = false;

sortedDropzoneLayouts.forEach((dropzone, index) => {
const inX = gesture.moveX > dropzone.x && gesture.moveX < dropzone.x + dropzone.width;
const inY = gesture.moveY > dropzone.y && gesture.moveY < dropzone.y + dropzone.height;
if (inX && inY) {
inDropzone = dropzone;
inDropzone.index = index;
}
});
return inDropzone;
}

swapItems(fromIndex, toIndex) {
console.log('swapping', fromIndex, '<->', toIndex);
const { items, dropzones } = this.state;
this.setState({
items: swap(items, fromIndex, toIndex),
dropzones: swap(dropzones, fromIndex, toIndex),
});
}

render() {
console.log(this.state);
return (
<View style={styles.container}>
{this.state.items.map((item, index) =>
<Draggable key={index}
item={item}
index={index}
addDropzone={this.addDropzone.bind(this)}
inDropzone={this.inDropzone.bind(this)}
swapItems={this.swapItems.bind(this)}
/>
)}
</View>
);
}
}

const styles = StyleSheet.create({
container: {
flex: 1,
paddingTop: 60,
backgroundColor: 'orange',
justifyContent: 'center',
alignItems: 'center',
flexDirection: 'row',
flexWrap: 'wrap',
},
dropzone: {
// margin: 5,
zIndex: -1,
width: 106,
height: 106,
borderColor: 'green',
borderWidth: 3,
backgroundColor: 'lightgreen',
},
draggable: {
backgroundColor: 'white',
justifyContent: 'center',
alignItems: 'center',
width: 100,
height: 100,
borderWidth: 1,
borderColor: 'black'
},
image: {
width: 75,
height: 75
}
});

export default Playground;

EDIT3:所以以上在模拟器中效果很好,但在实际的 iPhone 上非常慢。开始时需要很长时间才能拖动某些内容(约 3 秒)并在交换项目时卡住(约 1 秒)。试图找出原因(可能是我糟糕的实现排序/循环数组的方式太多了,但不知道该怎么做)。我简直不敢相信它在实际手机上的速度要慢得多。

最新:我只是要学习/使用这些实现 https://github.com/ollija/react-native-sortable-grid , https://github.com/fangwei716/30-days-of-react-native#day-18找出我做错了什么。他们很难找到(否则我不会从头开始做这个并发布这个问题),所以我希望这可以帮助那些试图为他们的应用做同样事情的人!

最佳答案

首先对于性能问题,我建议使用 Direct Manipulation .当你想转换你的图像时,你需要用 setNativeProps 来做:

this.refs['YOUR_IMAGE'].setNativeProps({style: {
transform: [{ translateX }, { translateY }, { rotate }, { scale }],
}});

在 react-native 中,我们有两个领域,JavaScript 和 Native 端,我们在它们之间架起了一座桥梁。

这是理解 React Native 性能的主要关键之一。每个领域本身都很快。当我们从一个领域移动到另一个领域时,经常会出现性能瓶颈。为了构建高性能的 React Native 应用程序,我们必须将过桥的次数保持在最低限度。

您可以通过示例阅读更多 here .

其次,查看您的性能监视器(摇动您的设备或 Command-D 并选择“显示性能监视器”)。重要的部分是 View ,上面的数字是你在屏幕上的 View 数量,底部的数字通常更大,但通常表明你有一些可以改进/重构的东西。

关于react-native - 拖放和交换项目动画?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40326822/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com