Search Results for

    Show / Hide Table of Contents

    Animations API - Animate Appended Point

    SciChart library has several built-in animations which you can use to animate your Renderable Series.

    Note

    Please refer to the Animations API article for more details.

    Also, you can create a custom animation and have complete control over data appearing on the screen. This tutorial shows how to animate the last appended point to a FastLineRenderableSeries in real-time.

    To achieve that we'd need to perform 2 steps:

    • Create a new transformation - a class that implements a IRenderPassDataTransformation protocol.
    • Animate your series using previously created transformation

    Note

    A complete project of the Animated Line Series example you can find in the SciChart Android Examples Suite as well as on GitHub:

    • Native Example

    Create transformation

    Creating transformation is fairly simple. We have to create a class that implements an IRenderPassDataTransformation protocol and pass an ISeriesRenderPassData type suitable for your Renderable Series. In our case we will subclass an abstract class BaseRenderPassDataTransformation<TRenderPassData>, pass LineRenderPassData type and implement few required methods. The code would look like follows:

    • Java
    • Java with Builders API
    • Kotlin
    class AppendedPointTransformation extends BaseRenderPassDataTransformation<LineRenderPassData> {
        private final FloatValues originalXCoordinates = new FloatValues();
        private final FloatValues originalYCoordinates = new FloatValues();
    
        protected AppendedPointTransformation() {
            super(LineRenderPassData.class);
        }
    
        @Override
        protected void saveOriginalData() {
            if (!renderPassData.isValid()) return;
    
            TransformationHelpers.copyData(renderPassData.xCoords, originalXCoordinates);
            TransformationHelpers.copyData(renderPassData.yCoords, originalYCoordinates);
        }
    
        @Override
        protected void applyTransformation() {
            if (!renderPassData.isValid()) return;
    
            int count = renderPassData.pointsCount();
            float currentTransformationValue = getCurrentTransformationValue();
    
            float xStart;
            if (count <= 1) {
                xStart = renderPassData.getXCoordinateCalculator().getCoordinate(0.0);
            } else {
                xStart = originalXCoordinates.get(count - 2);
            }
            float xFinish = originalXCoordinates.get(count - 1);
            float additionalX = xStart + (xFinish - xStart) * currentTransformationValue;
            renderPassData.xCoords.set(count - 1, additionalX);
    
            float yStart;
            if (count <= 1) {
                yStart = renderPassData.getYCoordinateCalculator().getCoordinate(0.0);
            } else {
                yStart = originalYCoordinates.get(count - 2);
            }
            float yFinish = originalYCoordinates.get(count - 1);
            float additionalY = yStart + (yFinish - yStart) * currentTransformationValue;
            renderPassData.yCoords.set(count - 1, additionalY);
        }
    
        @Override
        protected void discardTransformation() {
            TransformationHelpers.copyData(originalXCoordinates, renderPassData.xCoords);
            TransformationHelpers.copyData(originalYCoordinates, renderPassData.yCoords);
        }
    
        @Override
        protected void onInternalRenderPassDataChanged() {
            applyTransformation();
        }
    }
    
    class AppendedPointTransformation extends BaseRenderPassDataTransformation<LineRenderPassData> {
        private final FloatValues originalXCoordinates = new FloatValues();
        private final FloatValues originalYCoordinates = new FloatValues();
    
        protected AppendedPointTransformation() {
            super(LineRenderPassData.class);
        }
    
        @Override
        protected void saveOriginalData() {
            if (!renderPassData.isValid()) return;
    
            TransformationHelpers.copyData(renderPassData.xCoords, originalXCoordinates);
            TransformationHelpers.copyData(renderPassData.yCoords, originalYCoordinates);
        }
    
        @Override
        protected void applyTransformation() {
            if (!renderPassData.isValid()) return;
    
            int count = renderPassData.pointsCount();
            float currentTransformationValue = getCurrentTransformationValue();
    
            float xStart;
            if (count <= 1) {
                xStart = renderPassData.getXCoordinateCalculator().getCoordinate(0.0);
            } else {
                xStart = originalXCoordinates.get(count - 2);
            }
            float xFinish = originalXCoordinates.get(count - 1);
            float additionalX = xStart + (xFinish - xStart) * currentTransformationValue;
            renderPassData.xCoords.set(count - 1, additionalX);
    
            float yStart;
            if (count <= 1) {
                yStart = renderPassData.getYCoordinateCalculator().getCoordinate(0.0);
            } else {
                yStart = originalYCoordinates.get(count - 2);
            }
            float yFinish = originalYCoordinates.get(count - 1);
            float additionalY = yStart + (yFinish - yStart) * currentTransformationValue;
            renderPassData.yCoords.set(count - 1, additionalY);
        }
    
        @Override
        protected void discardTransformation() {
            TransformationHelpers.copyData(originalXCoordinates, renderPassData.xCoords);
            TransformationHelpers.copyData(originalYCoordinates, renderPassData.yCoords);
        }
    
        @Override
        protected void onInternalRenderPassDataChanged() {
            applyTransformation();
        }
    }
    
    class AppendedPointTransformation : BaseRenderPassDataTransformation<LineRenderPassData>(LineRenderPassData::class.java) {
        private val originalXCoordinates = FloatValues()
        private val originalYCoordinates = FloatValues()
    
        override fun saveOriginalData() {
            if (!renderPassData.isValid) return
    
            TransformationHelpers.copyData(renderPassData.xCoords, originalXCoordinates)
            TransformationHelpers.copyData(renderPassData.yCoords, originalYCoordinates)
        }
    
        override fun applyTransformation() {
            if (!renderPassData.isValid) return
    
            val count = renderPassData.pointsCount()
            val currentTransformationValue = currentTransformationValue
    
            val xStart: Float
            xStart = if (count <= 1) {
                renderPassData.xCoordinateCalculator.getCoordinate(0.0)
            } else {
                originalXCoordinates[count - 2]
            }
            val xFinish = originalXCoordinates[count - 1]
            val additionalX = xStart + (xFinish - xStart) * currentTransformationValue
            renderPassData.xCoords[count - 1] = additionalX
    
            val yStart: Float
            yStart = if (count <= 1) {
                renderPassData.yCoordinateCalculator.getCoordinate(0.0)
            } else {
                originalYCoordinates[count - 2]
            }
            val yFinish = originalYCoordinates[count - 1]
            val additionalY = yStart + (yFinish - yStart) * currentTransformationValue
            renderPassData.yCoords[count - 1] = additionalY
        }
    
        override fun discardTransformation() {
            TransformationHelpers.copyData(originalXCoordinates, renderPassData.xCoords)
            TransformationHelpers.copyData(originalYCoordinates, renderPassData.yCoords)
        }
    
        override fun onInternalRenderPassDataChanged() {
            applyTransformation()
        }
    }
    

    Animate series

    With the transformation created above, all we need to do is just animate our series. It's easily achievable with AnimationsHelper APIs like below:

    • Java
    • Java with Builders API
    • Kotlin
    // Append new value to our dataSeries in real-time
    yValue = random.nextDouble() * MAX_Y_VALUE;
    dataSeries.append(currentXValue, yValue);
    
    // Animate renderable series with our custom transformation
    AnimationsHelper.createAnimator(
            rSeries,
            new AppendedPointTransformation(),
            ANIMATION_DURATION,
            0,
            new DecelerateInterpolator(),
            new FloatEvaluator(),
            0f, 1f
    ).start();
    
    // Append new value to our dataSeries in real-time
    yValue = random.nextDouble() * MAX_Y_VALUE;
    dataSeries.append(currentXValue, yValue);
    
    // Animate renderable series with our custom transformation
    AnimationsHelper.createAnimator(
            rSeries,
            new AppendedPointTransformation(),
            ANIMATION_DURATION,
            0,
            new DecelerateInterpolator(),
            new FloatEvaluator(),
            0f, 1f
    ).start();
    
    
    // Append new value to our dataSeries in real-time
    yValue = random.nextDouble() * MAX_Y_VALUE
    dataSeries.append(currentXValue, yValue)
    
    // Animate renderable series with our custom transformation
    AnimationsHelper.createAnimator(
        rSeries,
        AppendedPointTransformation(),
        ANIMATION_DURATION,
        0,
        DecelerateInterpolator(),
        FloatEvaluator(),
        0f, 1f
    ).start()
    
    Note

    You may also take a look at the Animations API - Animate Updated Point article to find out how to animate Series after updating existing data points.

    Back to top © 2011-2025 SciChart. All rights reserved. | sitemap.xml