本文介绍了反应与比例相关的原生动画旋转圆的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧! 问题描述 我有一个动画组件,您可以在其中选择17个圆圈之一。到目前为止看起来像这样: 我想要添加一个动画,该动画在圆离中心越来越近时对其进行缩放。我该怎么做? 直到现在,我一直试图将圆的x值计算为 Math.sin(index * deltaTheta * Math.PI / 180 + Math.PI)* Radius 并在映射到比例因子(例如高斯)的函数中使用此值。之所以失败是因为x值没有改变,因为我使用CSS变换旋转。 然后我尝试使用其他内插范围,但未取得令人满意的结果。 我的代码: import React,{组件} from'react' import {Text,View,PanResponder,Animated,Dimensions} from'react -native'从'styled-components'样式化的导入导入'./Circle'的圆圈 const SCREEN_WIDTH = Dimensions.get('window')。width const Container = styled(Animated.View)` margin:自动; 宽度:200像素; 高度:200像素; 职位:相对; top:100像素; ` const gaussFunc =(x,sigma,mu)=> { return 1 / sigma / Math.sqrt(2.0 * Math.PI)* Math.exp(-1.0 / 2.0 * Math.pow((x-mu)/ sigma,2))} const myGaussFunc =(x)=> gaussFunc(x,1/2 / Math.sqrt(2 * Math.PI),0) const circle = [{ color:'red'},{ 颜色:蓝色 },{颜色:绿色 },{颜色:黄色 },{颜色:紫色 },{颜色:黑色 },{颜色:灰色 },{颜色:粉红色 },{颜色:石灰 },{颜色:深绿色 },{颜色: '深红色'},{颜色:'橙色'},{颜色:'青色'},{颜色:'海军'},{颜色:'indigo'},{颜色:'brown'},{颜色:'peru'} ] 函数withFunction(callback){让inputRange = [],outputRange = [],steps = 50; ///输入范围0-1 for(let i = 0; i let key = i / steps; inputRange.push(key); outputRange.push(callback(key)); } return {inputRange,outputRange}; } 出口默认类SDGCircle扩展组件{ state = { deltaTheta:360 / circles.length,半径:0,//半径中心圆(contaienr)的半径半径:25,//轨道运行半径容器:{高度:0,宽度:0}, deltaAnim:new Animated.Value(0), } 偏移量=()=> parseInt(this.state.container.width / 2)-this.state.radius _panResponder = PanResponder.create({ nMoveShouldSetPanResponderCapture:(evt,gestureState)=> true, onMoveShouldSetPanResponder:(事件,手势状态)=> true, onPanResponderGrant:()=> { const {deltaAnim} = this.state deltaAnim.setOffset(deltaAnim._value ) deltaAnim.setValue(0)}, onPanResponderMove :(事件,手势状态)= >> { const {deltaAnim,scaleAnim,deltaTheta,Radius} = this.state deltaAnim.setValue(gestureState.dx) console.log(deltaAnim)}, onPanResponderRelease :(事件,手势状态)=> { const {dx,vx} =手势状态 const {deltaAnim} = this.state deltaAnim.flattenOffset() Animated.spring(deltaAnim,{ toValue :this.getIthCircleValue(dx,deltaAnim),摩擦:5,十ion:10,})。start(()=> this.simplifyOffset(deltaAnim._value)); } }) getIthCircleValue =(dx,deltaAnim)=> { const selectedCircle = Math.round(deltaAnim._value /(600 / circles.length)) return(selectedCircle)* 600 / circles.length } getAmountForNextSlice =( dx,offset)=> { //这会四舍五入到最接近的200,以将圆捕捉到正确的三分之一。 const snappedOffset = this.snapOffset(offset); //根据方向,我们可以加200或减200来计算新的偏移位置。 (200等于120deg!) // const newOffset = dx> 0? snappedOffset + 200:snappedOffset-200; //固定3圈 const newOffset = dx> 0? snappedOffset + 600 / circles.length:snappedOffset-600 / circles.length; 返回newOffset; } snapOffset =(offset)=> {return Math.round(offset /(600 / circles.length))* 600 / circles.length; } simpleOffset =(val)=> { const {deltaAnim} = this.state if(deltaAnim._offset> 600)deltaAnim.setOffset(deltaAnim._offset-600) if(deltaAnim._offset< -600) deltaAnim.setOffset(deltaAnim._offset + 600)} handleLayout =({native事件})=> { this.setState({半径:nativeEvent.layout.width,容器:{高度:nativeEvent.layout.height,宽度:nativeEvent.layout .width } })} render(){ const {deltaAnim,radius} = this.state return(<容器 onLayout = {this.handleLayout} {... this._panResponder.panHandlers} style = {{ transform: [{旋转:deltaAnim.interpolate({ inputRange:[-200,0,200], outputRange:['-120deg','0deg','120deg'] }} }] }} > {circles.map((circle,index)= >> { const {deltaTheta,Radius} = this.state return(< Circle key = {index} color = {circle.color} radius = {radius} 样式= {{剩余:Math.sin(index * deltaTheta * Math.PI / 180 + Math.PI)* Radius + this.offset(),顶部:Math.cos(index * deltaTheta * Math.PI / 180 + Math.PI)* Radius + this.offset(),}} > <文本样式= {{颜色:‘白色’}}> {索引}< /文本> < /圆> )})} < / Container> )} } 解决方案 源代码如下: import React,{组件} from'react' import {Text,View, PanResponder,动画,尺寸}从'react-native'导入,从'styled-components'样式化导入从'./Circle'的圆圈 const SCREEN_WIDTH = Dimensions.get ('window')。width const Container = styled(Animated.View)` margin:auto; 宽度:200像素; 高度:200像素; 职位:相对; top:100像素; ` const gaussFunc =(x,sigma,mu)=> { return 1 / sigma / Math.sqrt(2.0 * Math.PI)* Math.exp(-1.0 / 2.0 * Math.pow((x-mu)/ sigma,2))} const myGaussFunc =(x)=> gaussFunc(x,1/2 / Math.sqrt(2 * Math.PI),0) const circle = [{ color:'red'},{ 颜色:蓝色 },{颜色:绿色 },{颜色:黄色 },{颜色:紫色 },{颜色:黑色 },{颜色:灰色 },{颜色:粉红色 },{颜色:石灰 },{颜色:深绿色 },{颜色: '深红色'},{颜色:'橙色'},{颜色:'青色'},{颜色:'海军'},{颜色:'indigo'},{颜色:'brown'},{颜色:'peru'}] 函数withFunction(callback){让inputRange = [],outputRange = [],步长= 50; ///输入范围0-1 for(let i = 0; i let key = i / steps; inputRange.push(key); outputRange.push(callback(key)); } return {inputRange,outputRange}; } 出口默认类SDGCircle扩展组件{构造函数(props){超级(props) const deltaTheta = 360 / circle.length const pxPerDeg = 200/120 const thetas = [] for(const i in circle){ let val = i * deltaTheta * pxPerDeg if(i> = 9) val =-(circles.length-i)* deltaTheta * pxPerDeg thetas.push(val)} this.state = { deltaTheta,半径:0,//圆心半径(contaienr)半径:25,//半径轨道的圆形容器的容器:{高度:0,宽度:0}, deltaAnim:新的Animated.Value(0), thetas, thetasAnim:thetas.map(theta => new Animated.Value(theta)),} } offset =()=> parseInt(this.state.container.width / 2)-this.state.radius _panResponder = PanResponder.create({ nMoveShouldSetPanResponderCapture:(evt,gestureState)=> true, onMoveShouldSetPanResponder:(event,gestureState)=> true, onPanResponderGrant:()=> { const {deltaAnim,thetasAnim,thetas} = this.state deltaAnim.setOffset (deltaAnim._value) deltaAnim.setValue(0) const iSel = Math.round((deltaAnim._value + deltaAnim._offset)/(600 / circles.length)) for(让i = 0; i 让xi = i + iSel if(xi> 16) xi-= circle.length if(xi< 0) xi + = circle.length try { thetasAnim [xi] .setOffset(thetas [i])} catch(err ){console.log(xi)} } }, onPanResponderMove :(事件,手势状态)=> { const {deltaAnim,scaleAnim,deltaTheta ,Radius,thetasAnim} = this.state deltaAnim.setValue(gestureState.dx) for(thetasAnim theta){ theta.setValue(-gestureState.dx)} }, onPanResponderRelease :(事件,手势状态)=> { const {dx,vx} =手势状态 const {deltaAnim,thetasAnim,deltaTheta,thetas} = this.state deltaAnim.flattenOffset() const ithCircleValue = this.getIthCircleValue(dx,deltaAnim) Animated.spring(deltaAnim,{ toValue:ithCircleValue,摩擦力:5,张力:10,}) .start(()=> { this.simplifyOffset(deltaAnim)}); } }) getIthCircleValue =(dx,deltaAnim)=> { const selectedCircle = Math.round((deltaAnim._value + deltaAnim._offset)/(600 / circles.length)) return(selectedCircle)* 600 / circles.length } snapOffset =(偏移)=> {return Math.round(offset /(600 / circles.length))* 600 / circles.length; } simpleOffset =(anim)=> { if(anim._value + anim._offset> = 600)anim.setOffset(anim._offset-600) if(anim._value + anim._offset< = -600)动画。 setOffset(anim._offset + 600)} handleLayout =({nativeEvent})=> { this.setState({半径:nativeEvent.layout.width,容器:{高度:nativeEvent.layout.height,宽度:nativeEvent.layout .width } })} render(){ const {deltaAnim,radius} = this.state return(<容器 onLayout = {this.handleLayout} {... this._panResponder.panHandlers} style = {{ transform: [{旋转:deltaAnim.interpolate({ inputRange:[-200,0,200], outputRange:['-120deg','0deg','120deg'] })}] }} > {circles.map((circle,index)=> { const {deltaTheta,thetasAnim,半径} = this.state / * const difInPx = index * deltaTheta * 200/120 * / 让我=索引 / * if(index> = Math。 round(circles.length / 2))* / / * i = circle.length-index * / scale = thetasAnim [i] .interpolate({ inputRange:[-300,0,300], outputRange: [0,2,0],}) return(< Circle key = {index} color = {circle.color } radius = {radius} style = {{ left:Math.sin(index * deltaTheta * Math.PI / 180 + Math.PI)* Radius + this.offset() ,顶部:Math.cos(index * deltaTheta * Math.PI / 180 + Math.PI)* Radius + this.offset(),转换:[{scale}], }} > <文本样式= {{颜色:‘白色’}}> {索引}< /文本> < /圆> )})} < / Container> )} } I have an animated component where you can select one of seventeen circles. It looks like this so far: I would like to add an animation that scales the circle as it gets closer to the center. How do I do that?Until now I tried to calculate the x value of the circle as Math.sin(index*deltaTheta*Math.PI/180 + Math.PI)*Radius and use this value in a functions which maps to a scaling factor (e.g. a gaussian). This fails because the x value does not change, because I am using CSS transform rotate.Then I tried to use a different interpolating range for every single circle, but did not achieved a satisfying result.My code:import React, { Component } from 'react'import { Text, View, PanResponder, Animated, Dimensions } from 'react-native'import styled from 'styled-components'import Circle from './Circle'const SCREEN_WIDTH = Dimensions.get('window').widthconst Container = styled(Animated.View)` margin: auto; width: 200px; height: 200px; position: relative; top: 100px;`const gaussFunc = (x, sigma, mu) => { return 1/sigma/Math.sqrt(2.0*Math.PI)*Math.exp(-1.0/2.0*Math.pow((x-mu)/sigma,2))}const myGaussFunc = (x) => gaussFunc(x, 1/2/Math.sqrt(2*Math.PI), 0)const circles = [{ color: 'red'}, { color: 'blue'}, { color: 'green'}, { color: 'yellow'}, { color: 'purple'}, { color: 'black'}, { color: 'gray'}, { color: 'pink'}, { color: 'lime'}, { color: 'darkgreen'}, { color: 'crimson'}, { color: 'orange'}, { color: 'cyan'}, { color: 'navy'}, { color: 'indigo'}, { color: 'brown'}, { color: 'peru'} ]function withFunction(callback) { let inputRange = [], outputRange = [], steps = 50; /// input range 0-1 for (let i=0; i<=steps; ++i) { let key = i/steps; inputRange.push(key); outputRange.push(callback(key)); } return { inputRange, outputRange };}export default class SDGCircle extends Component { state = { deltaTheta: 360/circles.length, Radius: 0, // radius of center circle (contaienr) radius: 25, // radius of orbiting circles container: { height: 0, width: 0 }, deltaAnim: new Animated.Value(0), } offset = () => parseInt(this.state.container.width/2)-this.state.radius _panResponder = PanResponder.create({ nMoveShouldSetPanResponderCapture: (evt, gestureState) => true, onMoveShouldSetPanResponder: (event, gestureState) => true, onPanResponderGrant: () => { const { deltaAnim } = this.state deltaAnim.setOffset(deltaAnim._value) deltaAnim.setValue(0) }, onPanResponderMove: (event, gestureState) => { const { deltaAnim, scaleAnim, deltaTheta, Radius } = this.state deltaAnim.setValue(gestureState.dx) console.log(deltaAnim) }, onPanResponderRelease: (event, gestureState) => { const {dx, vx} = gestureState const {deltaAnim} = this.state deltaAnim.flattenOffset() Animated.spring(deltaAnim, { toValue: this.getIthCircleValue(dx, deltaAnim), friction: 5, tension: 10, }).start(() => this.simplifyOffset(deltaAnim._value)); } }) getIthCircleValue = (dx, deltaAnim) => { const selectedCircle = Math.round(deltaAnim._value/(600/circles.length)) return (selectedCircle)*600/circles.length } getAmountForNextSlice = (dx, offset) => { // This just rounds to the nearest 200 to snap the circle to the correct thirds const snappedOffset = this.snapOffset(offset); // Depending on the direction, we either add 200 or subtract 200 to calculate new offset position. (200 are equal to 120deg!) // const newOffset = dx > 0 ? snappedOffset + 200 : snappedOffset - 200; // fixed for 3 circles const newOffset = dx > 0 ? snappedOffset + 600/circles.length : snappedOffset - 600/circles.length; return newOffset; } snapOffset = (offset) => { return Math.round(offset / (600/circles.length)) * 600/circles.length; } simplifyOffset = (val) => { const { deltaAnim } = this.state if(deltaAnim._offset > 600) deltaAnim.setOffset(deltaAnim._offset - 600) if(deltaAnim._offset < -600) deltaAnim.setOffset(deltaAnim._offset + 600) } handleLayout = ({ nativeEvent }) => { this.setState({ Radius: nativeEvent.layout.width, container: { height: nativeEvent.layout.height, width: nativeEvent.layout.width } }) } render() { const {deltaAnim, radius} = this.state return ( <Container onLayout={this.handleLayout} {...this._panResponder.panHandlers} style={{ transform: [{ rotate: deltaAnim.interpolate({ inputRange: [-200, 0, 200], outputRange: ['-120deg', '0deg', '120deg'] }) }] }} > {circles.map((circle, index) => { const {deltaTheta, Radius} = this.state return ( <Circle key={index} color={circle.color} radius={radius} style={{ left: Math.sin(index*deltaTheta*Math.PI/180 + Math.PI)*Radius+this.offset(), top: Math.cos(index*deltaTheta*Math.PI/180 + Math.PI)*Radius+this.offset(), }} > <Text style={{color: 'white'}}>{index}</Text> </Circle> ) })} </Container> ) }} 解决方案 FYI: I got a solution. The result looks like this:and the source code is given by:import React, { Component } from 'react'import { Text, View, PanResponder, Animated, Dimensions } from 'react-native'import styled from 'styled-components'import Circle from './Circle'const SCREEN_WIDTH = Dimensions.get('window').widthconst Container = styled(Animated.View)` margin: auto; width: 200px; height: 200px; position: relative; top: 100px;`const gaussFunc = (x, sigma, mu) => { return 1/sigma/Math.sqrt(2.0*Math.PI)*Math.exp(-1.0/2.0*Math.pow((x-mu)/sigma,2))}const myGaussFunc = (x) => gaussFunc(x, 1/2/Math.sqrt(2*Math.PI), 0)const circles = [{ color: 'red'}, { color: 'blue'}, { color: 'green'}, { color: 'yellow'}, { color: 'purple'}, { color: 'black'}, { color: 'gray'}, { color: 'pink'}, { color: 'lime'}, { color: 'darkgreen'}, { color: 'crimson'}, { color: 'orange'}, { color: 'cyan'}, { color: 'navy'}, { color: 'indigo'}, { color: 'brown'}, { color: 'peru'}]function withFunction(callback) { let inputRange = [], outputRange = [], steps = 50; /// input range 0-1 for (let i=0; i<=steps; ++i) { let key = i/steps; inputRange.push(key); outputRange.push(callback(key)); } return { inputRange, outputRange };}export default class SDGCircle extends Component { constructor(props) { super(props) const deltaTheta = 360/circles.length const pxPerDeg = 200/120 const thetas = [] for (const i in circles) { let val = i*deltaTheta*pxPerDeg if(i >= 9) val = -(circles.length-i)*deltaTheta*pxPerDeg thetas.push(val) } this.state = { deltaTheta, Radius: 0, // radius of center circle (contaienr) radius: 25, // radius of orbiting circles container: { height: 0, width: 0 }, deltaAnim: new Animated.Value(0), thetas, thetasAnim: thetas.map(theta => new Animated.Value(theta)), } } offset = () => parseInt(this.state.container.width/2)-this.state.radius _panResponder = PanResponder.create({ nMoveShouldSetPanResponderCapture: (evt, gestureState) => true, onMoveShouldSetPanResponder: (event, gestureState) => true, onPanResponderGrant: () => { const { deltaAnim, thetasAnim, thetas } = this.state deltaAnim.setOffset(deltaAnim._value) deltaAnim.setValue(0) const iSel = Math.round((deltaAnim._value+deltaAnim._offset)/(600/circles.length)) for(let i=0; i<circles.length; i++) { let xi = i+iSel if(xi > 16) xi -= circles.length if(xi < 0) xi += circles.length try { thetasAnim[xi].setOffset(thetas[i]) } catch(err) {console.log(xi)} } }, onPanResponderMove: (event, gestureState) => { const { deltaAnim, scaleAnim, deltaTheta, Radius, thetasAnim } = this.state deltaAnim.setValue(gestureState.dx) for (theta of thetasAnim) { theta.setValue(-gestureState.dx) } }, onPanResponderRelease: (event, gestureState) => { const {dx, vx} = gestureState const {deltaAnim, thetasAnim, deltaTheta, thetas} = this.state deltaAnim.flattenOffset() const ithCircleValue = this.getIthCircleValue(dx, deltaAnim) Animated.spring(deltaAnim, { toValue: ithCircleValue, friction: 5, tension: 10, }).start(() => { this.simplifyOffset(deltaAnim) }); } }) getIthCircleValue = (dx, deltaAnim) => { const selectedCircle = Math.round((deltaAnim._value+deltaAnim._offset)/(600/circles.length)) return (selectedCircle)*600/circles.length } snapOffset = (offset) => { return Math.round(offset / (600/circles.length)) * 600/circles.length; } simplifyOffset = (anim) => { if(anim._value + anim._offset >= 600) anim.setOffset(anim._offset - 600) if(anim._value + anim._offset <= -600) anim.setOffset(anim._offset + 600) } handleLayout = ({ nativeEvent }) => { this.setState({ Radius: nativeEvent.layout.width, container: { height: nativeEvent.layout.height, width: nativeEvent.layout.width } }) } render() { const {deltaAnim, radius} = this.state return ( <Container onLayout={this.handleLayout} {...this._panResponder.panHandlers} style={{ transform: [{ rotate: deltaAnim.interpolate({ inputRange: [-200, 0, 200], outputRange: ['-120deg', '0deg', '120deg'] }) }] }} > {circles.map((circle, index) => { const {deltaTheta, thetasAnim, Radius} = this.state /* const difInPx = index*deltaTheta*200/120 */ let i = index /* if(index >= Math.round(circles.length/2)) */ /* i = circles.length - index */ scale = thetasAnim[i].interpolate({ inputRange: [-300, 0, 300], outputRange: [0, 2, 0], }) return ( <Circle key={index} color={circle.color} radius={radius} style={{ left: Math.sin(index*deltaTheta*Math.PI/180 + Math.PI)*Radius+this.offset(), top: Math.cos(index*deltaTheta*Math.PI/180 + Math.PI)*Radius+this.offset(), transform: [{ scale }], }} > <Text style={{color: 'white'}}>{index}</Text> </Circle> ) })} </Container> ) }} 这篇关于反应与比例相关的原生动画旋转圆的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!
10-14 15:20