मुख्य सामग्री पर जाएँ
CrossFly 100% ओपन सोर्सGitHub पर फ़ॉलो करें
CrossFlyCrossFly
शुरू करें
सभी उदाहरण
रेसिपीएनिमेशन162 पंक्तियाँ · 1 फ़ाइलें

Animation

Styles.animationDuration is the house value — match it, so the app moves at one speed. Anything the user waits through twice was too long the first time.

यह क्या दिखाता है

The three animations, and the question that picks between them.

Motion is a statement about what just happened, so the kind follows the event. Something arrived or left — fade. Something moved from here to there — position. Something is ongoing with no known end — loop. Motion that answers none of those is decoration, and decoration is the first thing a user reads as the app being slow.

None of the three owns a component. Each produces a number over time and hands it to you; what the number means is the screen's business. That is why the same LoopAnimation drives a colour here and could drive a radius, a rotation or an alpha elsewhere.

अपनी मशीन पर चलाएँ

हर उदाहरण एक साधारण Window है, इसलिए लॉन्चर को सिर्फ़ उसका नाम चाहिए।

gradlew lwjgl3:run --args="classname dev.cf.recipe.R22Animation"
इसे बदलना

Java पहले से संकलित होता है, इसलिए ब्राउज़र आपके बदलाव को खुद दोबारा नहीं बना सकता — यहाँ दिखा कोड प्रकाशित होने से पहले TeaVM द्वारा JavaScript में बदला गया था। फ़ाइल को किसी प्रोजेक्ट में कॉपी कीजिए और वह चारों प्लेटफ़ॉर्म पर बिना बदलाव चलती है; ऊपर दी गई कमांड ही पूरी तैयारी है।

यहीं चल रहा हैCrossFly 0.1.32
ऐप शुरू हो रहा है…

TeaVM ने इसे JavaScript में संकलित किया — यह वही कोड है, उसका वीडियो नहीं। · सभी उदाहरण

यह क्या इस्तेमाल करता है

इस उदाहरण में छुए गए फ़्रेमवर्क टाइप। जिनमें लिंक है उनका संदर्भ पृष्ठ मौजूद है।

AlphaAnimationButtonColorUtilsComponentGapLabelLoopAnimationPalColorPositionAnimationScreenStylesWidgets

सोर्स कोड

पूरा, बिना कटौती के। यह वही फ़ाइल है, उसका अंश नहीं।

कच्चा
dev/cf/recipe/R22Animation.java
package dev.cf.recipe;

import dev.cf.Tones;

import dev.crossfly.ui.*;
import dev.crossfly.ui.animation.*;
import dev.crossfly.ui.color.*;
import dev.crossfly.ui.color.ColorUtils;
import dev.crossfly.ui.graphics.*;
import dev.crossfly.ui.layout.*;
import dev.crossfly.ui.style.*;

import static dev.crossfly.ui.Widgets.*;

/**
 * The three animations, and the question that picks between them.
 *
 * <p>Motion is a statement about <b>what just happened</b>, so the kind follows the event. Something
 * arrived or left — fade. Something moved from here to there — position. Something is ongoing with
 * no known end — loop. Motion that answers none of those is decoration, and decoration is the first
 * thing a user reads as the app being slow.</p>
 *
 * <p>None of the three owns a component. Each produces a <b>number</b> over time and hands it to
 * you; what the number means is the screen's business. That is why the same {@link LoopAnimation}
 * drives a colour here and could drive a radius, a rotation or an alpha elsewhere.</p>
 */
public class R22Animation extends Recipe
{
   private Label fading, moving, pulsing;
   private LoopAnimation loop;

   public String title()
   {
      return "Animation";
   }

   public String note()
   {
      return "Styles.animationDuration is the house value — match it, so the app moves at one"
         + " speed. Anything the user waits through twice was too long the first time.";
   }

   public void build(Component body) throws Exception
   {
      head(body, 0xE65F, "Arriving and leaving", Tones.accent());
      fading = label("I fade out and back", 1.5f, true, Styles.primaryColor, Position.START);
      body.add(LAY_AUTO, outgap(fading, 0, 0, 0, .4f));
      button(body, "Fade it", this::fade);

      head(body, 0xE8D5, "Moving from here to there", Tones.support());
      moving = label("I slide", 1.5f, true, Styles.textColor, Position.START);
      moving.testTag("anim.slide");
      body.add(LAY_AUTO, outgap(moving, 0, 0, 0, .4f));
      button(body, "Slide it", this::slide);

      head(body, 0xE627, "Ongoing, with no end", Tones.highlight());
      pulsing = label("I pulse while something is happening", 1f, false, Styles.captionColor,
         Position.START);
      body.add(LAY_AUTO, outgap(pulsing, 0, 0, 0, .4f));
      button(body, "Start / stop", this::togglePulse);
   }

   private void fade()
   {
      final AlphaAnimation a = new AlphaAnimation(1f, .1f, Styles.animationDuration);
      a.onUpdate(() ->
      {
         // getValue() is the number the animation is at right now. Turning it into an alpha is
         // this screen's decision, not the animation's.
         fading.getStyle()
            .textColor(ColorUtils.withAlpha(Styles.primaryColor.color(), a.getValue()));
         Screen.repaint();
      });
      // Reversing on complete, so the label ends where it started and the demo can be run twice.
      a.onComplete(() ->
      {
         final AlphaAnimation back = new AlphaAnimation(.1f, 1f, Styles.animationDuration);
         back.onUpdate(() ->
         {
            fading.getStyle()
               .textColor(ColorUtils.withAlpha(Styles.primaryColor.color(), back.getValue()));
            Screen.repaint();
         });
         back.start();
      });
      a.start();
   }

   private void slide()
   {
      final PositionAnimation p = new PositionAnimation(0f, 8f, .45f);
      p.onUpdate(() ->
      {
         // An outgap in font units — so the distance is the same on every density, which a
         // distance in pixels is not.
         moving.getStyle()
            .outgap(Gap.ofFont(p.getValue(), 0, 0, 0));
         // invalidateMeasure, not setValidLayout(false): the outgap is read by the PARENT's layout,
         // and invalidating only the label left it where it was - "Slide it" did nothing (iPhone,
         // 17/10/2026).
         moving.invalidateMeasure();
         Screen.repaint();
      });
      p.onComplete(() ->
      {
         moving.getStyle()
            .outgap(Gap.ZERO);
         moving.invalidateMeasure();
         Screen.repaint();
      });
      p.start();
   }

   private void togglePulse()
   {
      if (loop != null && loop.isRunning())
      {
         loop.stop();
         pulsing.getStyle()
            .textColor(Styles.captionColor);
         Screen.repaint();
         return;
      }
      loop = new LoopAnimation(1.2f);
      loop.onUpdate(() ->
      {
         pulsing.getStyle()
            .textColor(ColorUtils.lerpColor(Styles.captionColor.color(),
               Styles.primaryColor.color(), loop.getValue()));
         Screen.repaint();
      });
      loop.start();
   }

   /**
    * Stopping what this screen started.
    *
    * <p>A loop has no end of its own — it runs until told otherwise. Leaving one running when the
    * screen closes gives you a component that repaints forever, holding its whole tree alive, and
    * the symptom is a frame rate that drops a little more with every screen the user visits.</p>
    */
   @Override
   public void onRemove()
   {
      super.onRemove();
      if (loop != null)
         loop.stop();
   }

   private void button(Component body, String text, Runnable r) throws Exception
   {
      Button b = new Button(text);
      b.onAction(c -> r.run());
      body.add(LAY_AUTO, outgap(b, 0, 0, 0, 1.2f));
   }

   private void head(Component body, int icone, String t, PalColor tom) throws Exception
   {
      body.add(LAY_AUTO, Tones.head(icone, t, tom));
   }
}
एआई सहायक के लिए

इस पृष्ठ के मशीन-पठनीय रूप, ताकि मॉडल API का अनुमान लगाने के बजाय उदाहरण पढ़ सके।

यह उदाहरण, JSON मेंयह उदाहरण, Markdown में
पिछलाCamera, barcode and GPSअगलाPainting your own