Rust+Slint 实现点击水波纹扩散效果按钮源码分享

Rust+Slint 实现点击水波纹扩散效果按钮源码分享

一、效果展示

二、源码分享

1、工程结构

2、main.rs

rust 复制代码
slint::include_modules!();

fn main() -> Result<(), slint::PlatformError> {
    let main_window = MainWindow::new()?;
    main_window.run()
}

3、build.rs

rust 复制代码
fn main() {
    // 编译 Slint UI
    slint_build::compile("ui/main.slint").unwrap();
}

4、main.slint

rust 复制代码
import { DiffusionButton } from "./diffusion_button.slint";

export component MainWindow inherits Window {
    preferred-width: 600px;
    preferred-height: 500px;
    title: "水波纹扩散按钮";
    background: @linear-gradient(135deg, #667eea 0%, #764ba2 100%);

    VerticalLayout {
        alignment: center;
        spacing: 40px;
        padding-left: 80px;
        padding-right: 80px;
        padding-top: 60px;
        padding-bottom: 60px;

        // 标题
        Text {
            text: "水波纹扩散按钮";
            font-size: 32px;
            font-weight: 700;
            color: white;
            horizontal-alignment: center;
            letter-spacing: 2px;
        }

        // 副标题
        Rectangle {
            height: 20px;
            background: transparent;
            Text {
                text: "点击按钮体验效果";
                font-size: 16px;
                color: rgba(255, 255, 255, 0.8);
                horizontal-alignment: center;
                vertical-alignment: center;
            }
        }

        // 按钮容器
        Rectangle {
            background: rgba(255, 255, 255, 0.95);
            border-radius: 24px;
            height: 240px;
            width: 320px;

            VerticalLayout {
                alignment: center;
                spacing: 30px;
                padding: 40px;

                DiffusionButton {
                    text: "按钮一";
                    button-width: 240px;
                    button-height: 56px;
                }

                DiffusionButton {
                    text: "按钮二";
                    button-width: 240px;
                    button-height: 56px;
                }
            }
        }
    }
}

5、diffusion_button.slint

rust 复制代码
export component DiffusionButton inherits Rectangle {
    // 输入属性
    in property <string> text;
    in property <length> button-width: 220px;
    in property <length> button-height: 60px;
    in property <color> button-bg: #ffffff;
    in property <color> ripple-color: #6366f1;  // 靛蓝色,更现代
    in property <color> text-color: #1f2937;  // 深灰色文字

    // 动画状态(内部属性)
    property <length> ripple-center-x: 0px;
    property <length> ripple-center-y: 0px;
    property <float> anim-progress: 0.0;
    property <bool> animating: false;
    property <bool> hovered: false;

    drop-shadow-color:#0edde4;
    drop-shadow-blur: 10px;

    // 派生属性
    property <length> ripple-size: root.button-width * 1.5 * root.anim-progress;
    property <float> ripple-opacity: 0.6 - (0.6 * root.anim-progress);

    // 按钮主体
    width: root.button-width;
    height: root.button-height;
    border-radius: root.button-height / 2;
    background: root.button-bg;
    border-width: 1px;
    border-color: hovered ? root.ripple-color : #e5e7eb;
    clip: true;

    // 悬停时的背景高亮
    Rectangle {
        width: root.width;
        height: root.height;
        border-radius: root.height / 2;
        background: root.hovered ? rgba(99, 102, 241, 0.05) : transparent;
    }

    // 水波纹圆形
    Rectangle {
        x: root.ripple-center-x - root.ripple-size / 2;
        y: root.ripple-center-y - root.ripple-size / 2;
        width: root.ripple-size;
        height: root.ripple-size;
        border-radius: root.ripple-size / 2;
        background: root.ripple-color;
        opacity: root.ripple-opacity;
    }

    // 按钮文字
    Text {
        text: root.text;
        font-size: 18px;
        font-weight: 600;
        letter-spacing: 1.5px;
        horizontal-alignment: center;
        vertical-alignment: center;
        color: root.text-color;
    }

    // 触摸区域
    touch := TouchArea {
        width: root.width;
        height: root.height;
        mouse-cursor: pointer;

        clicked => {
            root.ripple-center-x = self.mouse-x;
            root.ripple-center-y = self.mouse-y;
            root.anim-progress = 0.0;
            root.animating = true;
        }

        pointer-event => {
            root.hovered = self.has-hover;
        }
    }

    // 动画定时器
    Timer {
        interval: 16ms;
        running: root.animating;
        triggered => {
            root.anim-progress = root.anim-progress + (16.0 / 400.0);
            if root.anim-progress >= 1.0 {
                root.anim-progress = 1.0;
                root.animating = false;
            }
        }
    }
}

6、Cargo.toml

rust 复制代码
[package]
name = "sokoban"
version = "0.1.0"
edition = "2024"

[dependencies]
slint = "1.16.1"

[build-dependencies]
slint-build = "1.16.1"

三、实现原理

本水波纹扩散效果按钮的实现基于 Slint UI 框架 的声明式语法和动画系统,核心原理可分为以下几个部分:

1、组件结构与属性设计

DiffusionButton 组件继承自 Rectangle,通过属性(property)系统管理状态:

  • 输入属性 :如 text(按钮文字)、button-widthbutton-heightbutton-bgripple-color(水波纹颜色)、text-color,用于外部定制样式。
  • 内部动画状态属性
    • ripple-center-xripple-center-y:记录点击位置的坐标,决定水波纹扩散的中心点。
    • anim-progress:动画进度值(0.0 ~ 1.0),驱动水波纹大小和透明度的变化。
    • animating:布尔值,控制动画定时器的启停。
    • hovered:记录鼠标悬停状态,用于切换边框颜色和显示背景高亮。
  • 视觉增强属性
    • drop-shadow-colordrop-shadow-blur:为按钮添加投影效果,提升立体感。

2、 水波纹的绘制与动画

水波纹本身是一个 Rectangle(圆形),其关键属性由派生属性(property binding)实时计算:

rust 复制代码
property <length> ripple-size: root.button-width * 1.5 * root.anim-progress;
property <float> ripple-opacity: 0.6 - (0.6 * root.anim-progress);
  • ripple-size :水波纹直径随 anim-progress 线性增大,最大可达按钮宽度的 1.5 倍,确保波纹能完全覆盖按钮区域。
  • ripple-opacity:透明度随进度从 0.6 线性衰减到 0,实现波纹逐渐淡出的视觉效果。

圆形的位置通过 xy 绑定到 ripple-center-x/y 并减去半径,确保圆心始终位于点击点。

3、 交互与动画触发

  • 点击事件TouchAreaclicked 回调中,将当前鼠标坐标(self.mouse-x/y)赋值给 ripple-center-x/y,重置 anim-progress 为 0,并设置 animating = true 启动动画。
  • 悬停反馈pointer-event 回调更新 hovered 状态,用于切换边框颜色和显示半透明背景,提升交互感知。
  • 动画驱动Timer 以 16ms(约 60 FPS)的间隔运行,每次触发将 anim-progress 增加 16.0 / 400.0(即 400ms 完成整个动画)。当进度达到 1.0 时,停止定时器。

4、 视觉细节优化

  • 裁剪(clip) :根矩形设置 clip: true,确保水波纹不会溢出按钮的圆角边界。
  • 边框交互:悬停时边框颜色变为水波纹颜色,非悬停时为浅灰色,增强状态反馈。
  • 背景高亮 :悬停时添加一个半透明背景层(rgba(99, 102, 241, 0.05)),使按钮在鼠标移入时略有"浮起"感。
  • 投影效果 :通过 drop-shadow-colordrop-shadow-blur 为按钮添加柔和投影,增强立体感和现代感。

5、 整体工作流程

  1. 用户点击 → 记录点击坐标,重置动画进度,启动定时器。
  2. 定时器每帧更新anim-progress 增加,ripple-sizeripple-opacity 自动重新计算。
  3. UI 重绘 → 水波纹圆形的位置、大小、透明度随之变化,形成扩散并淡出的动画。
  4. 动画结束anim-progress 达到 1.0,定时器停止,水波纹保持最大尺寸并完全透明(不可见)。

这种实现方式完全在 Slint 的声明式系统 中完成,无需手动操作 DOM 或 CSS,体现了现代 UI 框架数据驱动响应式的设计优势。

相关推荐
后台模板学习2 小时前
Rust vs Python 在用户认证模块中的实现:底层
开发语言·python·rust
传奇开心果编程11 小时前
【Rust入门知识点学与练】第4课:条件判断 if / else
开发语言·学习·rust
2601_9622946116 小时前
AI 基础设施的“去 Python 化“:Rust 与 C# 的两条替代路径
ai·rust·c·基础设施·去python化
传奇开心果编程16 小时前
【Rust入门知识点学与练】第10课:Option 和 Result(错误处理入门)
开发语言·学习·rust
传奇开心果编程17 小时前
【Rust入门知识点学与练】第3课:String 与 &str 的区别
开发语言·学习·rust
tedcloud1231 天前
OpenLogi 怎么搭建?用 Rust 打造一个轻量的 Logitech 外设管理工具
linux·运维·服务器·开发语言·后端·rust·开源
传奇开心果编程1 天前
【Rust入门知识点学与练】第5课:函数
开发语言·学习·rust
传奇开心果编程1 天前
【Rust入门知识点学与练】第9课:Vec 动态数组
开发语言·学习·rust
yume_sibai1 天前
02-Rust 所有权与借用深入解析(底层原理 + 借用检查器 + 生命周期 + 内部可变性)
开发语言·后端·rust