反应具有比例依赖性的本机动画旋转圆圈

2024-02-06

我有一个动画组件,您可以在其中选择十七个圆圈之一。到目前为止看起来像这样:

我想添加一个动画,当圆靠近中心时,它会缩放圆。我怎么做?

到目前为止,我尝试将圆的 x 值计算为Math.sin(index*deltaTheta*Math.PI/180 + Math.PI)*Radius并在映射到缩放因子(例如高斯)的函数中使用该值。这失败了,因为 x 值没有改变,因为我正在使用 CSS 变换旋转。

然后我尝试使用不同的interpolating的范围内,但没有取得令人满意的结果。


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').width

const 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>
    )
  }
}

仅供参考:我找到了解决方案。结果如下:

源代码由以下给出:

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').width

const 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>
    )
  }
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

反应具有比例依赖性的本机动画旋转圆圈 的相关文章

随机推荐

  • 在 iPhone Objective C 中禁用 UIDatePicker 中的过去日期

    我是 Objective C 和 iPhone 开发的新手 我在 iPhone 应用程序中使用 UIDatePicker 我的要求是允许用户仅从 DatePicker 中选择未来日期 为此 我想仅在 DatePicker 中禁用过去的日期和
  • 相机或图库意图会破坏某些设备上的旧活动

    我正在开发使用 WebView 来显示其内容的应用程序 不过 需要打开相机或图库才能选择图片 Intent cameraIntent new Intent android provider MediaStore ACTION IMAGE C
  • Windows 7 中未加载 PHP 7 FTP 扩展

    我最近在 Windows 7 操作系统 32 位 中安装了 PHP 7 我使用 FTP 库 nicolab php ftp client 来实现 FTP 功能 但出现异常 致命错误 未捕获 FtpClient FtpException FT
  • SBT 未找到 scala 2.10.1 的 scalatest

    我正在尝试学习如何使用 SBT 但我发现以下简单示例无法找到 scalatest 的版本 name DoingItWrong version 0 0 1 scalaVersion 2 10 1 libraryDependencies Seq
  • 如何使用 Code First Entity Framework 4.1 获取完整对象

    我试图以 JSON 形式返回完全深层的对象 填充了所有外键关系 但我得到的所有引用对象均为空值 这是获取对象的调用 public ActionResult GetAll return Json ppEFContext Orders Json
  • 每个时间步更新 ODE 求解器中的初始条件

    我想要求解一个 ODE 系统 在前 30 000 秒内 我希望状态变量之一从相同的初始值开始 30 000 秒之后 我想将该状态变量的初始值更改为不同的值 并在其余时间模拟系统 这是我的代码 def ode rhs y t ydot 0 p
  • 数据库中的动态站点地图不显示节点

    我已经实现了这个https github com maartenba MvcSiteMapProvider wiki Defining sitemap nodes using IDynamicNodeProvider https githu
  • 为什么在本地 k8s 环境中使用 nginx-ingress 控制器和资源时有时需要编辑 /etc/hosts?

    不确定这是否特定于操作系统 但在我的 M1 Mac 上 我正在安装位于官方的 Nginx 控制器和资源示例控制器快速入门指南 https kubernetes github io ingress nginx deploy quick sta
  • jQuery DOM 操作效率 - 使用 JavaScript 构建整个页面

    我将从一个完全空白的页面开始 除了 html head 和 body 之外没有任何元素 然后使用 jQuery 构建页面 页面内容将采用来自 AJAX 请求的 JSON 形式 JSON 中的内容不会有任何 HTML 将根据 JSON 对象的
  • 使用 TypeScript 将箭头函数编译为常规函数

    这是一个非常简单的问题 但我还没有在任何地方找到答案 是否有一些开关可以使 TypeScript 将箭头函数编译为纯 JavaScript 函数 我在代码中经常使用它们 并且不想重写所有内容 但我最近意识到 IE 不支持它们 我已经尝试将脚
  • 创建一个全局类 Objective-c?

    我想在 Objective C 中创建一个具有已存储数据的类 以便访问数据时我不想实例化该类 我该怎么做 您可以使用单例 也可以使用仅由类方法组成并允许您访问静态数据的类 这是 ObjC 中的基本单例实现 interface MySingl
  • 如何修改此脚本以获取参数?

    我有一个结合了电源点的电源 shell 脚本 问题是它仅适用于当前目录 脚本所在的目录 中的电源点并将组合的电源点保存到文档中 如何更改脚本以从作为参数给出的任何目录运行 我像这样运行 power shell 脚本 Merge Presen
  • z 索引无法正常工作

    所以我正在制作一个网站 我有一个带有一些盒子阴影的顶部栏 然后我的正下方有一个描述框 因此 我设置了 z 索引以确保顶部栏 box shadow 会使用以下 css 覆盖描述框 topbar z index 9999 important d
  • 如何在汇编中实现 mod 运算符

    我正在学习汇编语言中的除法 根据我正在学习的书 idiv运算的结果放在eax中 余数放在edx中 书中的一个练习是实现number result divisor在装配中 我本以为这相当于正常的除法运算 除了 edx 是结果 然而这并没有起作
  • jQuery UI 可排序 - 对图像进行排序

    我刚刚为一组图像实现了 jQuery UI 可排序插件 我的标记如下 ul class ui sortable li img src images member 4698568 7884029 t jpg alt li li img src
  • WebBrowser 控件不会从 C# 打印

    我在 WinForms 应用程序上有一个 WebBrowser 控件 它正在加载转换为 HTML CSS 的 XML 如果我只想在那里或在常规浏览器中查看它 看起来很漂亮 当表单加载时 它应该导航到该文件 然后当 OnDocumentCom
  • 如何使用ssr在nuxt中添加ckeditor插件

    我正在尝试在我的通用 nuxt 应用程序中添加 ckeditor 5 的对齐插件 SSR 我在插件中尝试过这样 import Vue from vue import ClassicEditor from ckeditor ckeditor5
  • Jekyll编码类别特殊字符名称

    我的 Jekyll 安装曾经可以工作 自更新以来 我遇到了 URL 包含带有一些特殊字符的标签名称的问题 现在 当我尝试访问包含特殊字符的 URL 时 会收到一条错误消息 例如http 127 0 0 1 4000 tag Actualit
  • 最小成本强连通有向图

    我有一个强连接的有向图 即图 G 中的每对节点 i j 都有一条从 i 到 j 和 j 到 i 的路径 我希望从该图中找到一个强连通图 使得所有边的总和最小 换句话说 我需要以这样的方式删除边 即删除它们后 图仍然是强连接的 并且边总和的成
  • 反应具有比例依赖性的本机动画旋转圆圈

    我有一个动画组件 您可以在其中选择十七个圆圈之一 到目前为止看起来像这样 我想添加一个动画 当圆靠近中心时 它会缩放圆 我怎么做 到目前为止 我尝试将圆的 x 值计算为Math sin index deltaTheta Math PI 18