【问题标题】:Button border-radius transition in JavaFX/TornadoFXJavaFX/TornadoFX 中的按钮边框半径转换
【发布时间】:2019-12-14 09:13:11
【问题描述】:

我正在尝试让我的按钮在悬停时进行动画处理,例如 Discord 的那些。

我可以更改背景颜色和边框半径,但不能流畅地制作动画。 我只能找到动画形状的示例,而不是 css 属性。

这是我的按钮 css 代码。

class NavigatorButtonViewCss: Stylesheet() {
    companion object {
        val face by cssclass()

        val buttonBackgroundColor = c("#36393F")
        val buttonHoverBackgroundColor = c("#7289DA")
        val textColor = c("#C8C9CB")
    }

    init {
        indicator {
            prefWidth = 10.px
        }
        face {
            prefWidth = 50.px
            prefHeight = 50.px

            backgroundColor += buttonBackgroundColor
            backgroundRadius = multi(box(50.percent))

            label {
                textFill = textColor
            }

            and(hover) {
                // I want this to be animated
                backgroundColor += buttonHoverBackgroundColor
                backgroundRadius = multi(box(35.percent))
            }
        }
    }
}

我现在的按钮

我想要什么

有没有办法实现这种转变?

谢谢。

【问题讨论】:

    标签: javafx tornadofx


    【解决方案1】:

    JavaFX 不允许您通过 CSS 创建任何类型的动画,据我所知,即使使用 TornadoFX 也无法应用动画。 (请注意,我今天刚开始使用 TornadoFX,所以我可能错了。)

    通过样式表获取动画的唯一方法是将skin 设置为实现角动画的自定义皮肤。虽然你可以让皮肤提供控制圆角的 CSS 属性。

    通常你会扩展 Button 添加一个属性,但在这种情况下,我只是将它存储在 properties 映射中。

    分配给按钮的皮肤

    package org.example
    
    import com.sun.javafx.scene.control.skin.ButtonSkin
    
    import javafx.beans.binding.Bindings
    import javafx.beans.binding.DoubleBinding
    import javafx.beans.property.DoubleProperty
    import javafx.beans.property.SimpleDoubleProperty
    
    import java.util.function.Function
    import javafx.css.SimpleStyleableBooleanProperty
    import javafx.css.CssMetaData
    import javafx.css.StyleablePropertyFactory
    import javafx.css.Styleable
    import javafx.css.StyleableProperty
    import javafx.scene.control.Button
    import javafx.scene.shape.ArcTo
    import javafx.scene.shape.ClosePath
    import javafx.scene.shape.HLineTo
    import javafx.scene.shape.MoveTo
    import javafx.scene.shape.Path
    import javafx.scene.shape.VLineTo
    
    import tornadofx.*
    
    class AnimatedButtonSkin(button: Button) : ButtonSkin(button) {
    
        companion object {
            @JvmField
            val CSS_ROUNDED_KEY = "org.example.AnimatedButtonSkin.rounded"
            @JvmField
            val CSS_ROUNDED_METADATA: CssMetaData<Button, Boolean>
            @JvmField
            val FACTORY = StyleablePropertyFactory<Button>(javafx.scene.control.SkinBase.getClassCssMetaData())
    
            init {
                CSS_ROUNDED_METADATA = FACTORY.createBooleanCssMetaData(
                    "-fx-rounded",
                    object : Function<Button, StyleableProperty<kotlin.Boolean>> {
    
                        override fun apply(b: Button): StyleableProperty<Boolean> {
                            // property stored in properties to avoid extending button
                            val v = b.getProperties().get(CSS_ROUNDED_KEY)
                            return v as StyleableProperty<Boolean>
                        }
                    },
                    true
                )
            }
        }
    
        override fun dispose() {
            // get rid of the property and the shape
            val b = getSkinnable()
            b.getProperties().remove(CSS_ROUNDED_KEY)
            b.setShape(null)
    
            super.dispose()
        }
    
        private fun createArc(
            cornerSizeH: DoubleBinding,
            cornerSizeV: DoubleBinding,
            invertX: Boolean,
            invertY: Boolean
        ): ArcTo {
            return ArcTo().apply {
                setAbsolute(false)
                setSweepFlag(true)
                radiusXProperty().bind(cornerSizeH)
                radiusYProperty().bind(cornerSizeV)
                xProperty().bind(if (invertX) cornerSizeH.negate() else cornerSizeH)
                yProperty().bind(if (invertY) cornerSizeV.negate() else cornerSizeV)
            }
        }
    
        override fun getCssMetaData(): List<CssMetaData<out Styleable, *>>? {
            return FACTORY.getCssMetaData()
        }
    
        init {
            val prop = SimpleStyleableBooleanProperty(CSS_ROUNDED_METADATA, true)
            button.getProperties().put(CSS_ROUNDED_KEY, prop)
    
            // relative part of width/height that is rounded
            // size for single corner:
            //    0    -> rectangular button
            //    0.5  -> circular button
            val cornerSize = SimpleDoubleProperty(.5)
    
            val w = button.widthProperty()
            val h = button.heightProperty()
    
            // bindings for horizontal measures
            val cornerHSize = w.multiply(cornerSize)
            val doubleHCornerSize = cornerHSize.multiply(2.0);
    
            // measures for vertical measures
            val cornerVSize = h.multiply(cornerSize)
            val doubleVCornerSize = cornerVSize.multiply(2.0);
    
            // lower part of the top-left corner
            val start = MoveTo().apply {
                yProperty().bind(cornerSize);
            }
    
            // straight path of top
            val top = HLineTo().apply {
                setAbsolute(false)
                xProperty().bind(w.subtract(doubleHCornerSize))
            }
    
            // straight part of the right
            var right = VLineTo().apply {
                setAbsolute(false)
                yProperty().bind(h.subtract(doubleVCornerSize))
            }
    
            // straight part of the bottom
            val bottom = HLineTo().apply {
                setAbsolute(false)
                xProperty().bind(top.xProperty().negate())
            }
    
            // assemble the parts
            val shape = Path(
                start,
                createArc(cornerHSize, cornerVSize, false, true), top,
                createArc(cornerHSize, cornerVSize, false, false), right,
                createArc(cornerHSize, cornerVSize, true, false), bottom,
                createArc(cornerHSize, cornerVSize, true, true), ClosePath()
            )
            button.shape = shape
    
            // animate open/close on change of stylable property
            prop.addListener({ _, _, new -> cornerSize.animate(endValue = if (new) .5 else .2, duration = .2.seconds) })
        }
    
    }
    

    风格

    class NavigatorButtonViewCss: Stylesheet() {
        companion object {
            val face by cssclass()
    
            val rounded by cssproperty<Boolean>("-fx-rounded")
    
            val buttonBackgroundColor = c("#36393F")
            val buttonHoverBackgroundColor = c("#7289DA")
            val textColor = c("#C8C9CB")
        }
    
        init {
            indicator {
                prefWidth = 10.px
            }
            face {
                prefWidth = 50.px
                prefHeight = 50.px
    
                backgroundColor += buttonBackgroundColor
    
                textFill = textColor
    
                skin = AnimatedButtonSkin::class
    
                and(hover) {
                    rounded.value = false // update corners
                    backgroundColor += buttonHoverBackgroundColor
                }
            }
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2021-10-01
      • 1970-01-01
      • 2016-10-15
      • 2022-06-25
      • 2017-09-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多