为啥我的动画圈子的位置会闪烁?

Posted

技术标签:

【中文标题】为啥我的动画圈子的位置会闪烁?【英文标题】:Why does the locaton of my animated circles flicker?为什么我的动画圈子的位置会闪烁? 【发布时间】:2020-09-29 21:40:30 【问题描述】:

通过trident,我创建了一个(看似)简单的动画。一些圆圈正在从底部移动到顶部,然后通过正弦插值再次返回:

动画本身似乎可以正常工作,但是有一两帧我的所有球体都闪烁到最高位置。

为什么会闪烁?谁在用看似错误的值调用setY 方法?

我制作了一个测试类来重现该行为。您需要radiance-trident 3.0 才能使其工作:

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.geom.Ellipse2D;
import java.util.ArrayList;
import java.util.List;

import javax.swing.JDialog;
import javax.swing.JPanel;

import org.pushingpixels.trident.api.Timeline;
import org.pushingpixels.trident.api.Timeline.RepeatBehavior;
import org.pushingpixels.trident.api.ease.TimelineEase;
import org.pushingpixels.trident.api.swing.SwingRepaintTimeline;

public class MovingSpheresTest extends JDialog 

    private final double sphereRadius = 3d;

    private final double sphereCount = 12d;

    private final double helixHeight = 100d;

    private final double size = 200d;

    private final double animationSpeed = 0.5d;

    private List<CenteredSphere> spheres;

    private SwingRepaintTimeline repaintTimeline;

    private final JPanel contentPanel = new JPanel() 
        protected void paintComponent(Graphics g) 
            super.paintComponent(g);
            paintFrame((Graphics2D) g);
        

        private void paintFrame(Graphics2D g) 
            Graphics2D create = (Graphics2D) g.create();
            try 
                create.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
                create.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
                create.setColor(Color.BLACK);
                for (CenteredSphere centeredSphere : spheres) 
                    create.fill(centeredSphere);
                
             finally 
                create.dispose();
            
        
    ;

    /**
     * Launch the application.
     */
    public static void main(String[] args) 
        try 
            MovingSpheresTest dialog = new MovingSpheresTest();
            dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
            dialog.setVisible(true);
         catch (Exception e) 
            e.printStackTrace();
        
    

    /**
     * Create the dialog.
     */
    public MovingSpheresTest() 
        setBounds(100, 100, 450, 300);
        getContentPane().setLayout(new BorderLayout());
        contentPanel.setLayout(new FlowLayout());
        getContentPane().add(contentPanel, BorderLayout.CENTER);
        installSpheres();
        installRepaintTimeline();
    

    private void installSpheres() 
        double helixRadius = helixHeight / 2;
        double effectiveWidth = size - (2 * sphereRadius);
        double sphereDistance = effectiveWidth / (sphereCount - 1);
        double sphereCenterX = sphereRadius;
        double sphereCenterY = size / 2d;
        double sphereCenterYInitial = sphereCenterY - helixRadius;

        spheres = new ArrayList<>();
        for (int sphereIndex = 0; sphereIndex < sphereCount; sphereIndex++) 
            CenteredSphere sphere = new CenteredSphere(sphereCenterX, sphereRadius);
            spheres.add(sphere);
            sphereCenterX += sphereDistance;
            Timeline.builder()
                    .addPropertyToInterpolate(Timeline.<Double>property("y").on(sphere).from(sphereCenterYInitial)
                            .to(sphereCenterY + helixRadius))
                    .setEase(new FullSine((float) (sphereIndex * 2 * Math.PI / sphereCount)))
                    .setDuration((long) (animationSpeed * 3000d)).playLoop(RepeatBehavior.LOOP);
        

    

    private class FullSine implements TimelineEase 

        private float horizontalOffset;

        private FullSine(float horizontalOffset) 
            this.horizontalOffset = horizontalOffset;
        

        @Override
        public float map(float durationFraction) 
            return ((float) Math.sin(durationFraction * Math.PI * 2f + horizontalOffset) + 1f) / 2f;
        
    

    private void installRepaintTimeline() 
        repaintTimeline = SwingRepaintTimeline.repaintBuilder(contentPanel).build();
        repaintTimeline.playLoop(RepeatBehavior.LOOP);
    

    public class CenteredSphere extends Ellipse2D.Double 

        private double sphereCenterX;
        private double sphereRadius;

        public CenteredSphere(double sphereCenterX, double sphereRadius) 
            this.sphereCenterX = sphereCenterX;
            this.sphereRadius = sphereRadius;
        

        public void setY(double y) 
            setFrameFromCenter(sphereCenterX, y, sphereCenterX + sphereRadius, y + sphereRadius);
        
    


【问题讨论】:

【参考方案1】:

如上所述here 和固定here,

这很有趣。这是因为基本假设 TimelineEase 始终映射 [0.0-1.0] 区间而不“扭曲”端点。在这种特殊情况下,在每个动画循环期间,自定义 FullSine 用于根据球体偏移重新映射该间隔,但在循环重置期间,“结束”点不会被映射。

【讨论】:

以上是关于为啥我的动画圈子的位置会闪烁?的主要内容,如果未能解决你的问题,请参考以下文章

Css悬停动画闪烁/循环

是啥导致我的动画闪烁

闪烁动画问题

防止固定位置元素在 jQuery 动画期间闪烁

为啥我的应用在不录制时会显示正在录制(闪烁的红色)状态栏?

动画启动前,React路由器原生动画会闪烁