Android:如何实现View沿倒U型圆弧路径从(x1,y1)移动到(x2,y2)的动画
实现View沿倒U型圆弧路径的跳跃移动动画
核心思路
倒U型跳跃本质是让View沿上凸的圆弧轨迹移动,核心是先确定圆弧的几何参数(圆心、半径),再通过动画框架控制View沿轨迹逐帧更新位置。下面分两种常见实现方案,适合Android平台(基于Canvas、Path、动画API)。
方案一:Path + PathMeasure + ValueAnimator
通过Path构建倒U圆弧路径,用PathMeasure获取路径上的实时坐标,再通过ValueAnimator驱动View移动。
步骤&代码示例
- 定义基础参数(起点、终点、跳跃高度)
- 计算圆弧的圆心、半径(确保轨迹是上凸的倒U型)
- 构建圆弧路径
- 绑定动画并更新View位置
// 1. 基础参数定义 float startX = 100f, startY = 500f; float endX = 500f, endY = 500f; float jumpHeight = 150f; // 控制跳跃的高度 // 2. 计算圆弧几何参数 float midX = (startX + endX) / 2; float midY = (startY + endY) / 2; float halfHorizontalDist = Math.abs(endX - startX) / 2; // 勾股定理计算圆心到中点的垂直距离(圆心在两点连线下方,保证上凸) float verticalOffset = (float) Math.sqrt(jumpHeight * jumpHeight - halfHorizontalDist * halfHorizontalDist); float centerX = midX; float centerY = midY + verticalOffset; float radius = jumpHeight; // 3. 构建倒U型圆弧路径 Path jumpPath = new Path(); jumpPath.moveTo(startX, startY); // arcTo参数:矩形边界(包含圆弧的最小矩形)、起始角度、扫过角度、是否连接到起点 // 180度为左方向,扫过-180度即逆时针画上半圆,形成倒U jumpPath.arcTo( centerX - radius, centerY - radius, centerX + radius, centerY + radius, 180, -180, false ); // 4. 绑定动画 PathMeasure pathMeasure = new PathMeasure(jumpPath, false); float pathTotalLength = pathMeasure.getLength(); ValueAnimator animator = ValueAnimator.ofFloat(0f, 1f); animator.setDuration(1200); // 动画时长,单位毫秒 animator.addUpdateListener(animation -> { float progress = (float) animation.getAnimatedValue(); float[] currentPos = new float[2]; // 获取当前进度对应的路径坐标 pathMeasure.getPosTan(pathTotalLength * progress, currentPos, null); // 更新View位置(减去自身宽高的一半,让View中心对齐路径点) targetView.setX(currentPos[0] - targetView.getWidth() / 2); targetView.setY(currentPos[1] - targetView.getHeight() / 2); }); animator.start();
方案二:自定义TypeEvaluator + ObjectAnimator
直接通过几何公式计算圆弧上的实时坐标,无需构建Path,性能更高效。
步骤&代码示例
- 计算圆弧参数(同方案一)
- 自定义
TypeEvaluator,根据动画进度计算当前坐标 - 用
ObjectAnimator绑定自定义属性驱动动画
// 1. 基础参数与圆弧计算(同方案一) float startX = 100f, startY = 500f; float endX = 500f, endY = 500f; float jumpHeight = 150f; float midX = (startX + endX) / 2; float midY = (startY + endY) / 2; float halfHorizontalDist = Math.abs(endX - startX) / 2; float verticalOffset = (float) Math.sqrt(jumpHeight * jumpHeight - halfHorizontalDist * halfHorizontalDist); float centerX = midX; float centerY = midY + verticalOffset; float radius = jumpHeight; // 计算起始角度与总角度差(确保轨迹方向正确) float startAngle = (float) Math.toDegrees(Math.atan2(startY - centerY, startX - centerX)); float totalAngle = (float) Math.toDegrees(Math.atan2(endY - centerY, endX - centerX)) - startAngle; if (totalAngle > 0) totalAngle -= 360; // 修正为逆时针方向的角度差 // 2. 自定义Evaluator,根据进度计算圆弧上的点 TypeEvaluator<PointF> arcEvaluator = (fraction, startPoint, endPoint) -> { float currentAngle = startAngle + totalAngle * fraction; double radian = Math.toRadians(currentAngle); float currentX = centerX + radius * (float) Math.cos(radian); float currentY = centerY + radius * (float) Math.sin(radian); return new PointF(currentX, currentY); }; // 3. 绑定动画:需要给View添加setPosition方法(或用自定义属性) ObjectAnimator animator = ObjectAnimator.ofObject( targetView, "position", arcEvaluator, new PointF(startX, startY), new PointF(endX, endY) ); animator.setDuration(1200); animator.start(); // 给目标View添加setPosition方法(如果是自定义View可直接集成,否则用包装类) public void setPosition(PointF pos) { setX(pos.x - getWidth() / 2); setY(pos.y - getHeight() / 2); }
关键注意事项
- 轨迹适配:如果起点和终点不在同一水平线,需重新计算圆心位置,保证圆弧始终向上凸(可通过调整
verticalOffset的正负来控制凸向)。 - View对齐:更新位置时记得减去View自身宽高的一半,避免View左上角对齐路径点导致的偏移。
- 动画优化:如果是频繁触发的动画,优先用方案二(无Path构建开销);如果需要更复杂的轨迹(比如多段圆弧),方案一更灵活。
内容的提问来源于stack exchange,提问作者omkar_dharmesh
相关产品推荐
相关产品推荐

