├── .github
└── workflows
│ ├── check-coverage.yml
│ ├── public-api.yml
│ ├── record-coverage.yml
│ └── rust.yml
├── .gitignore
├── Cargo.lock
├── Cargo.toml
├── LICENSE
├── README.md
├── codecov.yml
├── examples
├── iced-demo
│ ├── Cargo.lock
│ ├── Cargo.toml
│ └── src
│ │ ├── OverusedGrotesk-Black.ttf
│ │ └── main.rs
├── iced-indicator
│ ├── Cargo.lock
│ ├── Cargo.toml
│ ├── resources
│ │ ├── check.svg
│ │ └── warn.svg
│ └── src
│ │ └── main.rs
├── iced-minimal
│ ├── Cargo.lock
│ ├── Cargo.toml
│ └── src
│ │ └── main.rs
└── macroquad-example
│ ├── Cargo.lock
│ ├── Cargo.toml
│ └── src
│ └── main.rs
└── src
├── animated.rs
├── lib.rs
├── public_api_test.rs
├── snapshots
└── lilt__public_api_test__public_api.snap
└── traits.rs
/.github/workflows/check-coverage.yml:
--------------------------------------------------------------------------------
1 | name: coverage diff
2 |
3 | on:
4 | pull_request:
5 | branches: [main]
6 |
7 | jobs:
8 | coverage:
9 | runs-on: ubuntu-latest
10 | steps:
11 | - uses: actions/checkout@v4
12 |
13 | - name: Install Rust
14 | run: rustup update stable
15 |
16 | - name: Install cargo-llvm-cov
17 | uses: taiki-e/install-action@cargo-llvm-cov
18 |
19 | - name: Generate PR coverage report
20 | run: cargo llvm-cov --json --summary-only > coverage-summary.json
21 |
22 | - name: Checkout coverage branch for baseline data
23 | uses: actions/checkout@v4
24 | with:
25 | ref: coverage
26 | path: coverage
27 |
28 | - name: Get latest baseline coverage report
29 | run: |
30 | latest=$(ls -t coverage/coverage-data/cov-action-*.json | head -n1)
31 | echo "Using baseline coverage report: $latest"
32 | cp "$latest" baseline_coverage.json
33 |
34 | - name: Compare coverage using shell script
35 | run: |
36 | pr_lines=$(jq '.data[0].totals.lines.percent' coverage-summary.json)
37 | base_lines=$(jq '.data[0].totals.lines.percent' baseline_coverage.json)
38 | echo "PR line coverage: $pr_lines%"
39 | echo "Baseline line coverage: $base_lines%"
40 | drop=$(echo "$base_lines - $pr_lines" | bc -l)
41 | echo "Coverage drop: $drop%"
42 | threshold=1.0
43 | if (( $(echo "$drop > $threshold" | bc -l) )); then
44 | echo "Coverage drop of $drop% exceeds allowed threshold of $threshold%."
45 | exit 1
46 | else
47 | echo "Coverage is within acceptable limits."
48 | fi
49 |
--------------------------------------------------------------------------------
/.github/workflows/public-api.yml:
--------------------------------------------------------------------------------
1 | name: public api
2 |
3 | on:
4 | pull_request:
5 | branches: ["main"]
6 |
7 | env:
8 | CARGO_TERM_COLOR: always
9 |
10 | concurrency:
11 | group: ${{ github.workflow }}-${{ github.ref }}
12 | cancel-in-progress: true
13 |
14 | jobs:
15 | build:
16 | name: check public api
17 | runs-on: ubuntu-latest
18 |
19 | steps:
20 | - uses: actions/checkout@v4
21 | - name: Run tests
22 | run: cargo test --verbose --features test-api
23 |
--------------------------------------------------------------------------------
/.github/workflows/record-coverage.yml:
--------------------------------------------------------------------------------
1 | name: record coverage
2 |
3 | on:
4 | push:
5 | branches: ["main"]
6 | workflow_dispatch:
7 |
8 | jobs:
9 | record-coverage:
10 | permissions:
11 | contents: write
12 | runs-on: ubuntu-latest
13 | steps:
14 | - uses: actions/checkout@v4
15 |
16 | - name: Install Rust
17 | run: rustup update stable
18 |
19 | - name: Install cargo-llvm-cov
20 | uses: taiki-e/install-action@cargo-llvm-cov
21 |
22 | - name: Run coverage and output JSON
23 | id: run_cov
24 | run: |
25 | set -euxo pipefail
26 | cargo llvm-cov --json --summary-only > coverage-summary.json
27 | cat coverage-summary.json
28 |
29 | - name: Determine branch name
30 | id: get_branch
31 | run: |
32 | set -euxo pipefail
33 | if [ -n "${GITHUB_HEAD_REF}" ]; then
34 | branch=${GITHUB_HEAD_REF}
35 | else
36 | branch=${GITHUB_REF#refs/heads/}
37 | fi
38 | echo "Branch is: $branch"
39 | echo "branch=${branch}" >> $GITHUB_OUTPUT
40 |
41 | - name: Checkout coverage branch
42 | uses: actions/checkout@v3
43 | with:
44 | ref: coverage
45 | token: ${{ secrets.GITHUB_TOKEN }}
46 | path: coverage_branch
47 |
48 | - name: Generate badge from template
49 | run: |
50 | COVERAGE=$(jq -r '.data[0].totals.lines.percent' coverage-summary.json)
51 | COVERAGE_ROUNDED=$(printf "%.0f" "$COVERAGE")
52 | COVERAGE_DISPLAY="${COVERAGE_ROUNDED}%"
53 | echo "Generating badge.svg with coverage: ${COVERAGE_DISPLAY}"
54 | sed "s/%%/${COVERAGE_DISPLAY}/g" coverage_branch/template.svg > coverage_branch/badge.svg
55 |
56 | - name: Save coverage JSON and badge for branch
57 | run: |
58 | set -euxo pipefail
59 | branch=${{ steps.get_branch.outputs.branch }}
60 | commit=${GITHUB_SHA}
61 | mkdir -p coverage_branch/coverage-data
62 | rm -f coverage_branch/coverage-data/${branch}-*.json || true
63 | cp coverage-summary.json "coverage_branch/coverage-data/${branch}-${commit}.json"
64 | cd coverage_branch
65 | git config user.name "github-actions[bot]"
66 | git config user.email "github-actions[bot]@users.noreply.github.com"
67 | git add -A
68 | if ! git diff --cached --quiet; then
69 | git commit -m "Update coverage for branch ${branch} at commit ${commit} on $(date -u)"
70 | git push origin coverage
71 | else
72 | echo "No changes to commit."
73 | fi
74 |
--------------------------------------------------------------------------------
/.github/workflows/rust.yml:
--------------------------------------------------------------------------------
1 | name: rust
2 |
3 | on:
4 | push:
5 | branches: ["main"]
6 | pull_request:
7 | branches: ["main"]
8 |
9 | env:
10 | CARGO_TERM_COLOR: always
11 |
12 | concurrency:
13 | group: ${{ github.workflow }}-${{ github.ref }}
14 | cancel-in-progress: true
15 |
16 | jobs:
17 | build:
18 | name: build & test 🧪
19 | runs-on: ubuntu-latest
20 |
21 | steps:
22 | - uses: actions/checkout@v4
23 | - name: Build
24 | run: cargo build --verbose
25 | - name: Run tests
26 | run: cargo test --verbose
27 | - name: Build examples
28 | run: |
29 | set -euxo pipefail
30 | for d in examples/*/ ; do
31 | echo "Building example in directory $d"
32 | (cd "$d" && CARGO_TARGET_DIR=../target cargo build --verbose)
33 | done
34 |
35 | clippy:
36 | name: clippy 🪢
37 | runs-on: ubuntu-latest
38 |
39 | steps:
40 | - uses: actions/checkout@v4
41 | - name: Run clippy
42 | run: cargo clippy -- --deny warnings
43 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | target/
2 | pkg/
3 | **/*.rs.bk
4 | dist/
5 | traces/
6 | *.DS_Store
7 | *.wasm
8 |
--------------------------------------------------------------------------------
/Cargo.lock:
--------------------------------------------------------------------------------
1 | # This file is automatically @generated by Cargo.
2 | # It is not intended for manual editing.
3 | version = 4
4 |
5 | [[package]]
6 | name = "bumpalo"
7 | version = "3.16.0"
8 | source = "registry+https://github.com/rust-lang/crates.io-index"
9 | checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
10 |
11 | [[package]]
12 | name = "camino"
13 | version = "1.1.9"
14 | source = "registry+https://github.com/rust-lang/crates.io-index"
15 | checksum = "8b96ec4966b5813e2c0507c1f86115c8c5abaadc3980879c3424042a02fd1ad3"
16 | dependencies = [
17 | "serde",
18 | ]
19 |
20 | [[package]]
21 | name = "cargo-manifest"
22 | version = "0.17.0"
23 | source = "registry+https://github.com/rust-lang/crates.io-index"
24 | checksum = "8b2ce2075c35e4b492b93e3d5dd1dd3670de553f15045595daef8164ed9a3751"
25 | dependencies = [
26 | "serde",
27 | "thiserror 1.0.69",
28 | "toml",
29 | ]
30 |
31 | [[package]]
32 | name = "cargo-platform"
33 | version = "0.1.9"
34 | source = "registry+https://github.com/rust-lang/crates.io-index"
35 | checksum = "e35af189006b9c0f00a064685c727031e3ed2d8020f7ba284d78cc2671bd36ea"
36 | dependencies = [
37 | "serde",
38 | ]
39 |
40 | [[package]]
41 | name = "cargo_metadata"
42 | version = "0.18.1"
43 | source = "registry+https://github.com/rust-lang/crates.io-index"
44 | checksum = "2d886547e41f740c616ae73108f6eb70afe6d940c7bc697cb30f13daec073037"
45 | dependencies = [
46 | "camino",
47 | "cargo-platform",
48 | "semver",
49 | "serde",
50 | "serde_json",
51 | "thiserror 1.0.69",
52 | ]
53 |
54 | [[package]]
55 | name = "cfg-if"
56 | version = "1.0.0"
57 | source = "registry+https://github.com/rust-lang/crates.io-index"
58 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
59 |
60 | [[package]]
61 | name = "console"
62 | version = "0.15.10"
63 | source = "registry+https://github.com/rust-lang/crates.io-index"
64 | checksum = "ea3c6ecd8059b57859df5c69830340ed3c41d30e3da0c1cbed90a96ac853041b"
65 | dependencies = [
66 | "encode_unicode",
67 | "libc",
68 | "once_cell",
69 | "windows-sys",
70 | ]
71 |
72 | [[package]]
73 | name = "encode_unicode"
74 | version = "1.0.0"
75 | source = "registry+https://github.com/rust-lang/crates.io-index"
76 | checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0"
77 |
78 | [[package]]
79 | name = "equivalent"
80 | version = "1.0.1"
81 | source = "registry+https://github.com/rust-lang/crates.io-index"
82 | checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
83 |
84 | [[package]]
85 | name = "hashbag"
86 | version = "0.1.12"
87 | source = "registry+https://github.com/rust-lang/crates.io-index"
88 | checksum = "98f494b2060b2a8f5e63379e1e487258e014cee1b1725a735816c0107a2e9d93"
89 |
90 | [[package]]
91 | name = "hashbrown"
92 | version = "0.15.2"
93 | source = "registry+https://github.com/rust-lang/crates.io-index"
94 | checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289"
95 |
96 | [[package]]
97 | name = "indexmap"
98 | version = "2.7.0"
99 | source = "registry+https://github.com/rust-lang/crates.io-index"
100 | checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f"
101 | dependencies = [
102 | "equivalent",
103 | "hashbrown",
104 | ]
105 |
106 | [[package]]
107 | name = "insta"
108 | version = "1.42.1"
109 | source = "registry+https://github.com/rust-lang/crates.io-index"
110 | checksum = "71c1b125e30d93896b365e156c33dadfffab45ee8400afcbba4752f59de08a86"
111 | dependencies = [
112 | "console",
113 | "linked-hash-map",
114 | "once_cell",
115 | "pin-project",
116 | "similar",
117 | ]
118 |
119 | [[package]]
120 | name = "itoa"
121 | version = "1.0.14"
122 | source = "registry+https://github.com/rust-lang/crates.io-index"
123 | checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674"
124 |
125 | [[package]]
126 | name = "js-sys"
127 | version = "0.3.76"
128 | source = "registry+https://github.com/rust-lang/crates.io-index"
129 | checksum = "6717b6b5b077764fb5966237269cb3c64edddde4b14ce42647430a78ced9e7b7"
130 | dependencies = [
131 | "once_cell",
132 | "wasm-bindgen",
133 | ]
134 |
135 | [[package]]
136 | name = "libc"
137 | version = "0.2.167"
138 | source = "registry+https://github.com/rust-lang/crates.io-index"
139 | checksum = "09d6582e104315a817dff97f75133544b2e094ee22447d2acf4a74e189ba06fc"
140 |
141 | [[package]]
142 | name = "lilt"
143 | version = "0.8.1"
144 | dependencies = [
145 | "insta",
146 | "public-api",
147 | "rustdoc-json",
148 | "rustup-toolchain",
149 | "web-time",
150 | ]
151 |
152 | [[package]]
153 | name = "linked-hash-map"
154 | version = "0.5.6"
155 | source = "registry+https://github.com/rust-lang/crates.io-index"
156 | checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f"
157 |
158 | [[package]]
159 | name = "log"
160 | version = "0.4.22"
161 | source = "registry+https://github.com/rust-lang/crates.io-index"
162 | checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
163 |
164 | [[package]]
165 | name = "memchr"
166 | version = "2.7.4"
167 | source = "registry+https://github.com/rust-lang/crates.io-index"
168 | checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
169 |
170 | [[package]]
171 | name = "once_cell"
172 | version = "1.20.2"
173 | source = "registry+https://github.com/rust-lang/crates.io-index"
174 | checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775"
175 |
176 | [[package]]
177 | name = "pin-project"
178 | version = "1.1.7"
179 | source = "registry+https://github.com/rust-lang/crates.io-index"
180 | checksum = "be57f64e946e500c8ee36ef6331845d40a93055567ec57e8fae13efd33759b95"
181 | dependencies = [
182 | "pin-project-internal",
183 | ]
184 |
185 | [[package]]
186 | name = "pin-project-internal"
187 | version = "1.1.7"
188 | source = "registry+https://github.com/rust-lang/crates.io-index"
189 | checksum = "3c0f5fad0874fc7abcd4d750e76917eaebbecaa2c20bde22e1dbeeba8beb758c"
190 | dependencies = [
191 | "proc-macro2",
192 | "quote",
193 | "syn",
194 | ]
195 |
196 | [[package]]
197 | name = "pin-project-lite"
198 | version = "0.2.15"
199 | source = "registry+https://github.com/rust-lang/crates.io-index"
200 | checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff"
201 |
202 | [[package]]
203 | name = "proc-macro2"
204 | version = "1.0.92"
205 | source = "registry+https://github.com/rust-lang/crates.io-index"
206 | checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0"
207 | dependencies = [
208 | "unicode-ident",
209 | ]
210 |
211 | [[package]]
212 | name = "public-api"
213 | version = "0.43.0"
214 | source = "registry+https://github.com/rust-lang/crates.io-index"
215 | checksum = "31529be2a00213a5eeca25ed983569db17036d90de2abe40c55aceaa0915795b"
216 | dependencies = [
217 | "hashbag",
218 | "rustdoc-types",
219 | "serde",
220 | "serde_json",
221 | "thiserror 2.0.11",
222 | ]
223 |
224 | [[package]]
225 | name = "quote"
226 | version = "1.0.37"
227 | source = "registry+https://github.com/rust-lang/crates.io-index"
228 | checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
229 | dependencies = [
230 | "proc-macro2",
231 | ]
232 |
233 | [[package]]
234 | name = "rustdoc-json"
235 | version = "0.9.4"
236 | source = "registry+https://github.com/rust-lang/crates.io-index"
237 | checksum = "b9cba50605762dcbc2e103a3a8b195c83b6978aca443c7dd6699ed2971c213e5"
238 | dependencies = [
239 | "cargo-manifest",
240 | "cargo_metadata",
241 | "serde",
242 | "thiserror 2.0.11",
243 | "toml",
244 | "tracing",
245 | ]
246 |
247 | [[package]]
248 | name = "rustdoc-types"
249 | version = "0.35.0"
250 | source = "registry+https://github.com/rust-lang/crates.io-index"
251 | checksum = "bf583db9958b3161d7980a56a8ee3c25e1a40708b81259be72584b7e0ea07c95"
252 | dependencies = [
253 | "serde",
254 | ]
255 |
256 | [[package]]
257 | name = "rustup-toolchain"
258 | version = "0.1.9"
259 | source = "registry+https://github.com/rust-lang/crates.io-index"
260 | checksum = "1b1146c7808061ba181ab8e2f0836e91b0cc2fd10a70e64dae5c55381ce6d6cf"
261 | dependencies = [
262 | "thiserror 2.0.11",
263 | ]
264 |
265 | [[package]]
266 | name = "ryu"
267 | version = "1.0.19"
268 | source = "registry+https://github.com/rust-lang/crates.io-index"
269 | checksum = "6ea1a2d0a644769cc99faa24c3ad26b379b786fe7c36fd3c546254801650e6dd"
270 |
271 | [[package]]
272 | name = "semver"
273 | version = "1.0.25"
274 | source = "registry+https://github.com/rust-lang/crates.io-index"
275 | checksum = "f79dfe2d285b0488816f30e700a7438c5a73d816b5b7d3ac72fbc48b0d185e03"
276 | dependencies = [
277 | "serde",
278 | ]
279 |
280 | [[package]]
281 | name = "serde"
282 | version = "1.0.215"
283 | source = "registry+https://github.com/rust-lang/crates.io-index"
284 | checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f"
285 | dependencies = [
286 | "serde_derive",
287 | ]
288 |
289 | [[package]]
290 | name = "serde_derive"
291 | version = "1.0.215"
292 | source = "registry+https://github.com/rust-lang/crates.io-index"
293 | checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0"
294 | dependencies = [
295 | "proc-macro2",
296 | "quote",
297 | "syn",
298 | ]
299 |
300 | [[package]]
301 | name = "serde_json"
302 | version = "1.0.138"
303 | source = "registry+https://github.com/rust-lang/crates.io-index"
304 | checksum = "d434192e7da787e94a6ea7e9670b26a036d0ca41e0b7efb2676dd32bae872949"
305 | dependencies = [
306 | "itoa",
307 | "memchr",
308 | "ryu",
309 | "serde",
310 | ]
311 |
312 | [[package]]
313 | name = "serde_spanned"
314 | version = "0.6.8"
315 | source = "registry+https://github.com/rust-lang/crates.io-index"
316 | checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1"
317 | dependencies = [
318 | "serde",
319 | ]
320 |
321 | [[package]]
322 | name = "similar"
323 | version = "2.7.0"
324 | source = "registry+https://github.com/rust-lang/crates.io-index"
325 | checksum = "bbbb5d9659141646ae647b42fe094daf6c6192d1620870b449d9557f748b2daa"
326 |
327 | [[package]]
328 | name = "syn"
329 | version = "2.0.90"
330 | source = "registry+https://github.com/rust-lang/crates.io-index"
331 | checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31"
332 | dependencies = [
333 | "proc-macro2",
334 | "quote",
335 | "unicode-ident",
336 | ]
337 |
338 | [[package]]
339 | name = "thiserror"
340 | version = "1.0.69"
341 | source = "registry+https://github.com/rust-lang/crates.io-index"
342 | checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
343 | dependencies = [
344 | "thiserror-impl 1.0.69",
345 | ]
346 |
347 | [[package]]
348 | name = "thiserror"
349 | version = "2.0.11"
350 | source = "registry+https://github.com/rust-lang/crates.io-index"
351 | checksum = "d452f284b73e6d76dd36758a0c8684b1d5be31f92b89d07fd5822175732206fc"
352 | dependencies = [
353 | "thiserror-impl 2.0.11",
354 | ]
355 |
356 | [[package]]
357 | name = "thiserror-impl"
358 | version = "1.0.69"
359 | source = "registry+https://github.com/rust-lang/crates.io-index"
360 | checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
361 | dependencies = [
362 | "proc-macro2",
363 | "quote",
364 | "syn",
365 | ]
366 |
367 | [[package]]
368 | name = "thiserror-impl"
369 | version = "2.0.11"
370 | source = "registry+https://github.com/rust-lang/crates.io-index"
371 | checksum = "26afc1baea8a989337eeb52b6e72a039780ce45c3edfcc9c5b9d112feeb173c2"
372 | dependencies = [
373 | "proc-macro2",
374 | "quote",
375 | "syn",
376 | ]
377 |
378 | [[package]]
379 | name = "toml"
380 | version = "0.8.19"
381 | source = "registry+https://github.com/rust-lang/crates.io-index"
382 | checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e"
383 | dependencies = [
384 | "indexmap",
385 | "serde",
386 | "serde_spanned",
387 | "toml_datetime",
388 | "toml_edit",
389 | ]
390 |
391 | [[package]]
392 | name = "toml_datetime"
393 | version = "0.6.8"
394 | source = "registry+https://github.com/rust-lang/crates.io-index"
395 | checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41"
396 | dependencies = [
397 | "serde",
398 | ]
399 |
400 | [[package]]
401 | name = "toml_edit"
402 | version = "0.22.22"
403 | source = "registry+https://github.com/rust-lang/crates.io-index"
404 | checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5"
405 | dependencies = [
406 | "indexmap",
407 | "serde",
408 | "serde_spanned",
409 | "toml_datetime",
410 | "winnow",
411 | ]
412 |
413 | [[package]]
414 | name = "tracing"
415 | version = "0.1.41"
416 | source = "registry+https://github.com/rust-lang/crates.io-index"
417 | checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0"
418 | dependencies = [
419 | "pin-project-lite",
420 | "tracing-attributes",
421 | "tracing-core",
422 | ]
423 |
424 | [[package]]
425 | name = "tracing-attributes"
426 | version = "0.1.28"
427 | source = "registry+https://github.com/rust-lang/crates.io-index"
428 | checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d"
429 | dependencies = [
430 | "proc-macro2",
431 | "quote",
432 | "syn",
433 | ]
434 |
435 | [[package]]
436 | name = "tracing-core"
437 | version = "0.1.33"
438 | source = "registry+https://github.com/rust-lang/crates.io-index"
439 | checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c"
440 | dependencies = [
441 | "once_cell",
442 | ]
443 |
444 | [[package]]
445 | name = "unicode-ident"
446 | version = "1.0.14"
447 | source = "registry+https://github.com/rust-lang/crates.io-index"
448 | checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83"
449 |
450 | [[package]]
451 | name = "wasm-bindgen"
452 | version = "0.2.99"
453 | source = "registry+https://github.com/rust-lang/crates.io-index"
454 | checksum = "a474f6281d1d70c17ae7aa6a613c87fce69a127e2624002df63dcb39d6cf6396"
455 | dependencies = [
456 | "cfg-if",
457 | "once_cell",
458 | "wasm-bindgen-macro",
459 | ]
460 |
461 | [[package]]
462 | name = "wasm-bindgen-backend"
463 | version = "0.2.99"
464 | source = "registry+https://github.com/rust-lang/crates.io-index"
465 | checksum = "5f89bb38646b4f81674e8f5c3fb81b562be1fd936d84320f3264486418519c79"
466 | dependencies = [
467 | "bumpalo",
468 | "log",
469 | "proc-macro2",
470 | "quote",
471 | "syn",
472 | "wasm-bindgen-shared",
473 | ]
474 |
475 | [[package]]
476 | name = "wasm-bindgen-macro"
477 | version = "0.2.99"
478 | source = "registry+https://github.com/rust-lang/crates.io-index"
479 | checksum = "2cc6181fd9a7492eef6fef1f33961e3695e4579b9872a6f7c83aee556666d4fe"
480 | dependencies = [
481 | "quote",
482 | "wasm-bindgen-macro-support",
483 | ]
484 |
485 | [[package]]
486 | name = "wasm-bindgen-macro-support"
487 | version = "0.2.99"
488 | source = "registry+https://github.com/rust-lang/crates.io-index"
489 | checksum = "30d7a95b763d3c45903ed6c81f156801839e5ee968bb07e534c44df0fcd330c2"
490 | dependencies = [
491 | "proc-macro2",
492 | "quote",
493 | "syn",
494 | "wasm-bindgen-backend",
495 | "wasm-bindgen-shared",
496 | ]
497 |
498 | [[package]]
499 | name = "wasm-bindgen-shared"
500 | version = "0.2.99"
501 | source = "registry+https://github.com/rust-lang/crates.io-index"
502 | checksum = "943aab3fdaaa029a6e0271b35ea10b72b943135afe9bffca82384098ad0e06a6"
503 |
504 | [[package]]
505 | name = "web-time"
506 | version = "1.1.0"
507 | source = "registry+https://github.com/rust-lang/crates.io-index"
508 | checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb"
509 | dependencies = [
510 | "js-sys",
511 | "wasm-bindgen",
512 | ]
513 |
514 | [[package]]
515 | name = "windows-sys"
516 | version = "0.59.0"
517 | source = "registry+https://github.com/rust-lang/crates.io-index"
518 | checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
519 | dependencies = [
520 | "windows-targets",
521 | ]
522 |
523 | [[package]]
524 | name = "windows-targets"
525 | version = "0.52.6"
526 | source = "registry+https://github.com/rust-lang/crates.io-index"
527 | checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
528 | dependencies = [
529 | "windows_aarch64_gnullvm",
530 | "windows_aarch64_msvc",
531 | "windows_i686_gnu",
532 | "windows_i686_gnullvm",
533 | "windows_i686_msvc",
534 | "windows_x86_64_gnu",
535 | "windows_x86_64_gnullvm",
536 | "windows_x86_64_msvc",
537 | ]
538 |
539 | [[package]]
540 | name = "windows_aarch64_gnullvm"
541 | version = "0.52.6"
542 | source = "registry+https://github.com/rust-lang/crates.io-index"
543 | checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
544 |
545 | [[package]]
546 | name = "windows_aarch64_msvc"
547 | version = "0.52.6"
548 | source = "registry+https://github.com/rust-lang/crates.io-index"
549 | checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
550 |
551 | [[package]]
552 | name = "windows_i686_gnu"
553 | version = "0.52.6"
554 | source = "registry+https://github.com/rust-lang/crates.io-index"
555 | checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
556 |
557 | [[package]]
558 | name = "windows_i686_gnullvm"
559 | version = "0.52.6"
560 | source = "registry+https://github.com/rust-lang/crates.io-index"
561 | checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
562 |
563 | [[package]]
564 | name = "windows_i686_msvc"
565 | version = "0.52.6"
566 | source = "registry+https://github.com/rust-lang/crates.io-index"
567 | checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
568 |
569 | [[package]]
570 | name = "windows_x86_64_gnu"
571 | version = "0.52.6"
572 | source = "registry+https://github.com/rust-lang/crates.io-index"
573 | checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
574 |
575 | [[package]]
576 | name = "windows_x86_64_gnullvm"
577 | version = "0.52.6"
578 | source = "registry+https://github.com/rust-lang/crates.io-index"
579 | checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
580 |
581 | [[package]]
582 | name = "windows_x86_64_msvc"
583 | version = "0.52.6"
584 | source = "registry+https://github.com/rust-lang/crates.io-index"
585 | checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
586 |
587 | [[package]]
588 | name = "winnow"
589 | version = "0.6.20"
590 | source = "registry+https://github.com/rust-lang/crates.io-index"
591 | checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b"
592 | dependencies = [
593 | "memchr",
594 | ]
595 |
--------------------------------------------------------------------------------
/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "lilt"
3 | version = "0.8.1"
4 | edition = "2024"
5 | description = " A simple library for running interruptable, transition based animations as a function of time."
6 | repository = "https://github.com/cyypherus/lilt"
7 | license = "MIT"
8 | keywords = ["animation", "interpolation"]
9 | authors = ["cyypherus"]
10 |
11 | [lib]
12 | crate-type = ["lib"]
13 |
14 | [features]
15 | test-api = []
16 |
17 | [dev-dependencies]
18 | insta = "1.42.1"
19 | public-api = "0.43.0"
20 | rustdoc-json = "0.9.4"
21 | rustup-toolchain = "0.1.9"
22 |
23 | [dependencies]
24 | web-time = "1.1.0"
25 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2024 ejjonny
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Lilt
2 |
3 |
4 |
5 | 
6 | 
7 | [](https://crates.io/crates/lilt)
8 | [](https://docs.rs/lilt)
9 | [](https://crates.io/crates/lilt)
10 | [](https://github.com/lilt/blob/master/LICENSE)
11 |
12 | A simple library for running interruptable, transition based animations as a function of time.
13 |
14 | This library only implements animations & would be most useful along with a GUI library that can do GUI things (like [iced](https://github.com/iced-rs/iced)).
15 |
16 | ## Getting Started
17 |
18 | ### Define
19 |
20 | Embed the state you want to animate in an `Animated` struct.
21 |
22 | ```rust
23 | struct MyViewState {
24 | toggle: Animated,
25 | }
26 | ```
27 |
28 | When you initialize your view state - define the initial state & configure the animation to your liking.
29 |
30 | ```rust
31 | let mut state = MyViewState {
32 | toggle: Animated::new(false)
33 | .duration(300.)
34 | .easing(Easing::EaseOut)
35 | .delay(30.)
36 | .repeat(3),
37 | };
38 | ```
39 |
40 | ### Transition
41 |
42 | When your state needs an update, call the `transition` function on your animated state, passing the current time.
43 |
44 | ```rust
45 | let now = std::time::Instant::now();
46 | state
47 | .toggle
48 | .transition(!state.animated_toggle.value, now);
49 | ```
50 |
51 | ### Render
52 |
53 | While rendering a view based on your state - use the `animate` function on your state to get the interpolated value for the current frame.
54 |
55 | ```rust
56 | let now = std::time::Instant::now();
57 |
58 | // The wrapped value can be used to interpolate any values that implement `Interpolable`
59 | let animated_width = self.toggle.animate_bool(100., 500., now);
60 |
61 | // If the wrapped value itself is `Interpolable`, it can easily be interpolated in place
62 | let animated_width = self.width.animate_wrapped(now);
63 |
64 | // There are plenty of `animate` methods for interpolating things based on the wrapped value.
65 | ```
66 |
67 | ### What's the point?
68 |
69 | lilt emerged from the need for ELM compatible / reactive animations.
70 |
71 | The animations modeled by this library don't require periodic mutation like a 'tick' function - all interim states of the animation are predefined when 'transition' is called, & then accessed while rendering based on the current time.
72 |
73 | lilt animations are fully independent of frame rate or tick frequency & only need to be computed if they're used during rendering.
74 |
75 | ## [Examples](examples/)
76 |
77 | Examples aren't included in lilt's manifest to reduce the overhead from large GUI crates while maintainers work on the main crate. To run examples, simply navigate to the desired example & run it as if it were a standalone crate with `cargo run`
78 |
79 | 
80 |
81 | ### Contributing
82 |
83 | This repo uses `cargo insta` to snapshot test the public API.
84 |
85 | If your PR changes the public API, one of the checks will fail by default.
86 |
87 | If the changes to the public API were intentional you can update the snapshot by running:
88 |
89 | `INSTA_UPDATE=always cargo test --features test-api`
90 |
--------------------------------------------------------------------------------
/codecov.yml:
--------------------------------------------------------------------------------
1 | comment:
2 | layout: "diff"
3 |
--------------------------------------------------------------------------------
/examples/iced-demo/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "iced-demo"
3 | version = "0.1.0"
4 | edition = "2021"
5 |
6 | [dependencies]
7 | iced = { version = "0.13.1", features = ["canvas", "tokio", "svg"] }
8 | lilt = { path = "../../" }
9 | iced_futures = "0.13.1"
10 |
--------------------------------------------------------------------------------
/examples/iced-demo/src/OverusedGrotesk-Black.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cyypherus/lilt/526ddfa4396c02dc09c844ec40aa811cc80f63d6/examples/iced-demo/src/OverusedGrotesk-Black.ttf
--------------------------------------------------------------------------------
/examples/iced-demo/src/main.rs:
--------------------------------------------------------------------------------
1 | use iced::font::Weight;
2 | use iced::widget::canvas::path::lyon_path::geom::euclid::Transform2D;
3 | use iced::widget::canvas::path::lyon_path::geom::Angle;
4 | use iced::widget::canvas::path::lyon_path::math::vector;
5 | use iced::widget::canvas::path::Arc;
6 | use iced::widget::canvas::{self, Frame, Geometry, Path, Program, Stroke};
7 | use iced::widget::{horizontal_space, text, vertical_space, Column, Container, Row, Space, Stack};
8 | use iced::window::frames;
9 | use iced::{mouse, Color, Font, Point, Rectangle, Renderer, Task};
10 | use iced::{Element, Length, Theme};
11 | use lilt::Animated;
12 | use lilt::Easing;
13 | use std::f32::consts::PI;
14 | use std::time::Instant;
15 |
16 | pub fn main() -> iced::Result {
17 | iced::application("Iced Demo", Example::update, Example::view)
18 | .font(include_bytes!("OverusedGrotesk-Black.ttf"))
19 | .subscription(Example::subscription)
20 | .run()
21 | }
22 |
23 | struct Example {
24 | spinner_trim: Animated,
25 | spinner_rotation: Animated,
26 | bars: Vec>,
27 | }
28 |
29 | #[derive(Debug, Clone, Copy)]
30 | enum AppMessage {
31 | Tick,
32 | }
33 |
34 | impl Default for Example {
35 | fn default() -> Self {
36 | Self::new()
37 | }
38 | }
39 |
40 | impl Example {
41 | fn new() -> Self {
42 | let time = std::time::Instant::now();
43 | let left: Vec> = (0..50)
44 | .map(|i| {
45 | Animated::new(false)
46 | .duration(800.)
47 | .easing(Easing::EaseInOutBounce)
48 | .delay(i as f32 * 30.)
49 | .repeat_forever()
50 | .auto_start(true, time)
51 | })
52 | .rev()
53 | .collect();
54 | let right: Vec> = (0..50)
55 | .map(|i| {
56 | Animated::new(false)
57 | .duration(800.)
58 | .easing(Easing::EaseInOutBounce)
59 | .delay(i as f32 * 30.)
60 | .repeat_forever()
61 | .auto_start(true, time)
62 | })
63 | .collect();
64 | Self {
65 | spinner_trim: Animated::new(false)
66 | .duration(900.)
67 | .repeat_forever()
68 | .auto_reverse()
69 | .auto_start(true, time),
70 | spinner_rotation: Animated::new(false)
71 | .easing(Easing::Linear)
72 | .duration(900.)
73 | .repeat_forever()
74 | .auto_start(true, time),
75 | bars: [left, right].concat(),
76 | }
77 | }
78 |
79 | fn subscription(&self) -> iced::Subscription {
80 | frames().map(|_| AppMessage::Tick)
81 | }
82 |
83 | fn update(&mut self, message: AppMessage) -> Task {
84 | match message {
85 | AppMessage::Tick => (),
86 | }
87 | Task::none()
88 | }
89 |
90 | fn view(&self) -> Element {
91 | let time = std::time::Instant::now();
92 | let mut overused_font: Font = Font::with_name("Overused Grotesk Roman");
93 | overused_font.weight = Weight::Black;
94 | Stack::new()
95 | .push(
96 | Row::new()
97 | .extend(
98 | self.bars
99 | .iter()
100 | .map(|b| {
101 | Container::new(Space::new(
102 | Length::Fill,
103 | b.animate_bool(10., 300., time),
104 | ))
105 | .style(move |_| {
106 | iced::widget::container::Style::default().background(
107 | Color::from_rgb8(
108 | b.animate_bool(0., 108., time) as u8,
109 | b.animate_bool(0., 74., time) as u8,
110 | b.animate_bool(0., 181., time) as u8,
111 | ),
112 | )
113 | })
114 | .into()
115 | })
116 | .collect::>(),
117 | )
118 | .height(Length::Fill)
119 | .align_y(iced::Alignment::Center),
120 | )
121 | .push(
122 | Column::new()
123 | .push(vertical_space())
124 | .push(
125 | Row::new()
126 | .push(horizontal_space())
127 | .push(text("lilt").font(overused_font).size(100.))
128 | .push(horizontal_space()),
129 | )
130 | .push(vertical_space()),
131 | )
132 | .push(
133 | Column::new()
134 | .push(vertical_space())
135 | .push(
136 | Row::new()
137 | .push(horizontal_space())
138 | .push(
139 | iced::widget::canvas(Spinner {
140 | trim: self.spinner_trim.animate_bool(0.5, 0., time),
141 | rotation: self.spinner_rotation.animate_bool(0., 1., time),
142 | })
143 | .height(Length::Fixed(250.))
144 | .width(Length::Fixed(250.)),
145 | )
146 | .push(horizontal_space()),
147 | )
148 | .push(vertical_space()),
149 | )
150 | .width(Length::Fill)
151 | .height(Length::Fill)
152 | .into()
153 | }
154 | }
155 |
156 | #[derive(Debug)]
157 | struct Spinner {
158 | trim: f32,
159 | rotation: f32,
160 | }
161 |
162 | impl Program for Spinner {
163 | type State = ();
164 |
165 | fn draw(
166 | &self,
167 | _state: &(),
168 | renderer: &Renderer,
169 | _theme: &Theme,
170 | bounds: Rectangle,
171 | _cursor: mouse::Cursor,
172 | ) -> Vec {
173 | let mut frame = Frame::new(renderer, bounds.size());
174 | let stroke = 30.;
175 | let radius = (f32::min(bounds.width, bounds.height) * 0.5) - (stroke * 0.5);
176 | let circle = Path::new(|p| {
177 | p.arc(Arc {
178 | center: Point::new(bounds.center_x() - bounds.x, bounds.center_y() - bounds.y),
179 | radius,
180 | start_angle: iced::Radians(0.),
181 | end_angle: iced::Radians(self.trim * 2. * PI),
182 | });
183 | })
184 | .transform(
185 | &Transform2D::identity()
186 | .then_translate(vector(-bounds.width * 0.5, -bounds.height * 0.5))
187 | .then_rotate(Angle::radians(self.rotation * 2. * PI))
188 | .then_translate(vector(bounds.width * 0.5, bounds.height * 0.5)),
189 | );
190 | // let _debug_frame = Path::rectangle(Point::ORIGIN, bounds.size());
191 | // frame.fill(&_debug_frame, Color::from_rgb8(255, 0, 0));
192 | frame.stroke(
193 | &circle,
194 | Stroke::default()
195 | .with_width(stroke)
196 | .with_line_cap(canvas::LineCap::Round)
197 | .with_color(Color::WHITE),
198 | );
199 | vec![frame.into_geometry()]
200 | }
201 | }
202 |
--------------------------------------------------------------------------------
/examples/iced-indicator/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "iced-demo"
3 | version = "0.1.0"
4 | edition = "2021"
5 |
6 | [dependencies]
7 | iced = { version = "0.13.1", features = ["canvas", "tokio", "svg"] }
8 | lilt = { path = "../../" }
9 |
--------------------------------------------------------------------------------
/examples/iced-indicator/resources/check.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/examples/iced-indicator/resources/warn.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/examples/iced-indicator/src/main.rs:
--------------------------------------------------------------------------------
1 | use iced::border::Radius;
2 | use iced::font::Weight;
3 | use iced::widget::canvas::path::lyon_path::geom::euclid::Transform2D;
4 | use iced::widget::canvas::path::lyon_path::geom::Angle;
5 | use iced::widget::canvas::path::lyon_path::math::vector;
6 | use iced::widget::canvas::path::Arc;
7 | use iced::widget::canvas::{self, Frame, Geometry, Path, Program, Stroke};
8 | use iced::widget::{center, container, svg, text, vertical_space, Container, Row, Stack};
9 | use iced::window::frames;
10 | use iced::{
11 | mouse, Background, Border, Color, Font, Point, Rectangle, Renderer, Subscription, Task,
12 | };
13 | use iced::{Element, Length, Theme};
14 | use lilt::{Animated, FloatRepresentable};
15 | use lilt::{Easing, Interpolable};
16 | use std::default::Default;
17 | use std::f32::consts::PI;
18 | use std::time::{Duration, Instant};
19 |
20 | pub fn main() -> iced::Result {
21 | iced::application("Iced Demo", Example::update, Example::view)
22 | .subscription(Example::subscription)
23 | .run()
24 | }
25 |
26 | #[derive(Debug, Clone, Copy, PartialEq, Eq)]
27 | enum IndicatorState {
28 | Analyzing,
29 | Safe,
30 | Warning,
31 | }
32 |
33 | impl FloatRepresentable for IndicatorState {
34 | fn float_value(&self) -> f32 {
35 | match self {
36 | IndicatorState::Analyzing => 0.,
37 | IndicatorState::Safe => 1.,
38 | IndicatorState::Warning => 2.,
39 | }
40 | }
41 | }
42 |
43 | struct Example {
44 | spinner_rotation: Animated,
45 | spinner_rotation_speed: Animated,
46 | indicator_state: Animated,
47 | }
48 |
49 | #[derive(Debug, Clone, Copy)]
50 | enum AppMessage {
51 | Tick,
52 | UpdateStatus,
53 | }
54 |
55 | impl Default for Example {
56 | fn default() -> Self {
57 | Self::new()
58 | }
59 | }
60 |
61 | impl Example {
62 | fn new() -> Self {
63 | let time = std::time::Instant::now();
64 | Self {
65 | spinner_rotation: Animated::new(false)
66 | .easing(Easing::Linear)
67 | .duration(300.)
68 | .repeat_forever()
69 | .auto_start(true, time),
70 | spinner_rotation_speed: Animated::new(false)
71 | .easing(Easing::EaseInOut)
72 | .duration(500.)
73 | .repeat_forever()
74 | .auto_reverse()
75 | .auto_start(true, time),
76 | indicator_state: Animated::new(IndicatorState::Analyzing)
77 | .easing(Easing::Custom(|l| {
78 | if l < 0.5 {
79 | Easing::EaseInOutCirc.value(l)
80 | } else {
81 | Easing::EaseInOutElastic.value(l)
82 | }
83 | }))
84 | .duration(200.),
85 | }
86 | }
87 |
88 | fn subscription(&self) -> iced::Subscription {
89 | Subscription::batch(vec![
90 | iced::time::every(std::time::Duration::from_millis(2000))
91 | .map(|_| AppMessage::UpdateStatus),
92 | frames().map(|_| AppMessage::Tick),
93 | ])
94 | }
95 |
96 | fn update(&mut self, message: AppMessage) -> Task {
97 | let time = std::time::Instant::now();
98 | match message {
99 | AppMessage::Tick => (),
100 | AppMessage::UpdateStatus => match self.indicator_state.value {
101 | IndicatorState::Analyzing => {
102 | self.indicator_state.transition(IndicatorState::Safe, time)
103 | }
104 | IndicatorState::Safe => self
105 | .indicator_state
106 | .transition(IndicatorState::Warning, time),
107 | IndicatorState::Warning => self
108 | .indicator_state
109 | .transition(IndicatorState::Analyzing, time),
110 | },
111 | }
112 | Task::none()
113 | }
114 |
115 | fn view(&self) -> Element {
116 | let time = std::time::Instant::now();
117 | let backing_color_analyzing = Color::from_rgb8(187, 218, 252);
118 | let fg_color_analyzing = Color::from_rgb8(96, 162, 241);
119 | let capsule_color_analyzing = Color::from_rgb8(230, 242, 254);
120 |
121 | let backing_color_warning = Color::from_rgb8(187, 218, 252);
122 | let fg_color_warning = Color::from_rgb8(235, 75, 67);
123 | let capsule_color_warning = Color::from_rgb8(250, 226, 227);
124 |
125 | let backing_color_safe = Color::from_rgb8(187, 218, 252);
126 | let fg_color_safe = Color::from_rgb8(96, 189, 93);
127 | let capsule_color_safe = Color::from_rgb8(220, 242, 220);
128 |
129 | let backing_color = self
130 | .indicator_state
131 | .animate::(
132 | |a| match a {
133 | IndicatorState::Safe => backing_color_safe.into(),
134 | IndicatorState::Warning => backing_color_warning.into(),
135 | IndicatorState::Analyzing => backing_color_analyzing.into(),
136 | },
137 | time,
138 | )
139 | .color;
140 | let fg_color = self
141 | .indicator_state
142 | .animate::(
143 | |a| match a {
144 | IndicatorState::Safe => fg_color_safe.into(),
145 | IndicatorState::Warning => fg_color_warning.into(),
146 | IndicatorState::Analyzing => fg_color_analyzing.into(),
147 | },
148 | time,
149 | )
150 | .color;
151 | let capsule_color = self
152 | .indicator_state
153 | .animate::(
154 | |a| match a {
155 | IndicatorState::Safe => capsule_color_safe.into(),
156 | IndicatorState::Warning => capsule_color_warning.into(),
157 | IndicatorState::Analyzing => capsule_color_analyzing.into(),
158 | },
159 | time,
160 | )
161 | .color;
162 |
163 | let height = 150.;
164 | let font = Font {
165 | weight: Weight::Bold,
166 | ..Default::default()
167 | };
168 | let warn_icon = svg(svg::Handle::from_memory(include_bytes!(
169 | "../resources/warn.svg"
170 | )));
171 | let check_icon = svg(svg::Handle::from_memory(include_bytes!(
172 | "../resources/check.svg"
173 | )));
174 |
175 | let icon_small = 0.2;
176 | let icon_big = 0.4;
177 |
178 | Container::new(center(
179 | Row::new()
180 | .push(
181 | Stack::new()
182 | .push(
183 | container(
184 | Row::new()
185 | .height(Length::Fixed(height))
186 | .push(vertical_space().width(Length::Fixed(height * 0.3)))
187 | .push(
188 | Stack::new()
189 | .width(Length::Fixed(height * 0.3))
190 | .height(Length::Fixed(height * 0.3))
191 | .push(
192 | iced::widget::canvas(Spinner {
193 | backing_color,
194 | spinning_color: fg_color,
195 | stroke: height * 0.06,
196 | rotation: self.spinner_rotation.animate(
197 | |v| if v { 0. } else { 1. },
198 | time.checked_add(Duration::from_millis(
199 | self.spinner_rotation_speed
200 | .animate_bool(0., 150., time)
201 | as u64,
202 | ))
203 | .unwrap_or(time),
204 | ),
205 | opacity: self.indicator_state.animate_if_eq(
206 | IndicatorState::Analyzing,
207 | 1.,
208 | 0.,
209 | time,
210 | ),
211 | })
212 | .width(Length::Fixed(
213 | height
214 | * self.indicator_state.animate_if_eq(
215 | IndicatorState::Analyzing,
216 | icon_big,
217 | icon_small,
218 | time,
219 | ),
220 | ))
221 | .height(Length::Fixed(
222 | height
223 | * self.indicator_state.animate_if_eq(
224 | IndicatorState::Analyzing,
225 | icon_big,
226 | icon_small,
227 | time,
228 | ),
229 | )),
230 | )
231 | .push(center(
232 | warn_icon
233 | .width(Length::Fixed(
234 | height
235 | * self.indicator_state.animate_if_eq(
236 | IndicatorState::Warning,
237 | icon_big,
238 | icon_small,
239 | time,
240 | ),
241 | ))
242 | .height(Length::Fixed(
243 | height
244 | * self.indicator_state.animate_if_eq(
245 | IndicatorState::Warning,
246 | icon_big,
247 | icon_small,
248 | time,
249 | ),
250 | ))
251 | .style(move |_, _| iced::widget::svg::Style {
252 | color: Some(fg_color_warning),
253 | })
254 | .opacity(self.indicator_state.animate_if_eq(
255 | IndicatorState::Warning,
256 | 1.,
257 | 0.,
258 | time,
259 | )),
260 | ))
261 | .push(center(
262 | check_icon
263 | .width(Length::Fixed(
264 | height
265 | * self.indicator_state.animate_if_eq(
266 | IndicatorState::Safe,
267 | icon_big,
268 | icon_small,
269 | time,
270 | ),
271 | ))
272 | .height(Length::Fixed(
273 | height
274 | * self.indicator_state.animate_if_eq(
275 | IndicatorState::Safe,
276 | icon_big,
277 | icon_small,
278 | time,
279 | ),
280 | ))
281 | .style(move |_, _| iced::widget::svg::Style {
282 | color: Some(fg_color_safe),
283 | })
284 | .opacity(self.indicator_state.animate_if_eq(
285 | IndicatorState::Safe,
286 | 1.,
287 | 0.,
288 | time,
289 | )),
290 | )),
291 | )
292 | .push(
293 | Stack::new()
294 | .width(Length::Fixed(
295 | self.indicator_state.animate::(
296 | |a| match a {
297 | IndicatorState::Safe => 150.,
298 | IndicatorState::Warning => 225.,
299 | IndicatorState::Analyzing => 700.,
300 | },
301 | time,
302 | ),
303 | ))
304 | .push(
305 | text("safe").font(font).size(height * 0.35).color(
306 | fg_color.scale_alpha(
307 | self.indicator_state.animate_if_eq(
308 | IndicatorState::Safe,
309 | 1.,
310 | 0.,
311 | time,
312 | ),
313 | ),
314 | ),
315 | )
316 | .push(
317 | text("warning")
318 | .font(font)
319 | .size(height * 0.35)
320 | .color(fg_color.scale_alpha(
321 | self.indicator_state.animate_if_eq(
322 | IndicatorState::Warning,
323 | 1.,
324 | 0.,
325 | time,
326 | ),
327 | )),
328 | )
329 | .push(
330 | text("analyzing transaction")
331 | .font(font)
332 | .size(height * 0.35)
333 | .color(fg_color.scale_alpha(
334 | self.indicator_state.animate_if_eq(
335 | IndicatorState::Analyzing,
336 | 1.,
337 | 0.,
338 | time,
339 | ),
340 | )),
341 | ),
342 | )
343 | .push(vertical_space().width(Length::Fixed(height * 0.3)))
344 | .align_y(iced::Alignment::Center)
345 | .spacing(height * 0.2),
346 | )
347 | .style(move |_| iced::widget::container::Style {
348 | border: Border {
349 | color: Color::BLACK,
350 | width: 0.,
351 | radius: Radius::new(height * 0.5),
352 | },
353 | background: Some(Background::Color(capsule_color)),
354 | ..Default::default()
355 | })
356 | .height(Length::Fixed(height))
357 | .width(Length::Shrink),
358 | )
359 | .width(Length::Shrink)
360 | .height(Length::Shrink),
361 | )
362 | .padding(30.),
363 | ))
364 | .style(move |_| iced::widget::container::Style {
365 | background: Some(Background::Color(Color::WHITE)),
366 | ..Default::default()
367 | })
368 | .into()
369 | }
370 | }
371 |
372 | impl From for InterpolableColor {
373 | fn from(value: Color) -> Self {
374 | Self { color: value }
375 | }
376 | }
377 |
378 | struct InterpolableColor {
379 | color: Color,
380 | }
381 |
382 | impl Interpolable for InterpolableColor {
383 | fn interpolated(&self, other: Self, ratio: f32) -> Self {
384 | let a = self.color;
385 | let b = other.color;
386 | let mix = Color::from_rgb(
387 | a.r + ((b.r - a.r) * ratio),
388 | a.g + ((b.g - a.g) * ratio),
389 | a.b + ((b.b - a.b) * ratio),
390 | );
391 | Self { color: mix }
392 | }
393 | }
394 |
395 | #[derive(Debug)]
396 | struct Spinner {
397 | backing_color: Color,
398 | spinning_color: Color,
399 | stroke: f32,
400 | rotation: f32,
401 | opacity: f32,
402 | }
403 |
404 | impl Program for Spinner {
405 | type State = ();
406 |
407 | fn draw(
408 | &self,
409 | _state: &(),
410 | renderer: &Renderer,
411 | _theme: &Theme,
412 | bounds: Rectangle,
413 | _cursor: mouse::Cursor,
414 | ) -> Vec {
415 | let mut frame = Frame::new(renderer, bounds.size());
416 | let stroke = self.stroke;
417 | let radius = (f32::min(bounds.width, bounds.height) * 0.5) - (stroke * 0.5);
418 | let backing = Path::new(|p| {
419 | p.arc(Arc {
420 | center: Point::new(bounds.center_x() - bounds.x, bounds.center_y() - bounds.y),
421 | radius,
422 | start_angle: iced::Radians(0.),
423 | end_angle: iced::Radians(2. * PI),
424 | });
425 | });
426 | let circle = Path::new(|p| {
427 | p.arc(Arc {
428 | center: Point::new(bounds.center_x() - bounds.x, bounds.center_y() - bounds.y),
429 | radius,
430 | start_angle: iced::Radians(0.),
431 | end_angle: iced::Radians(0.5 * PI),
432 | });
433 | })
434 | .transform(
435 | &Transform2D::identity()
436 | .then_translate(vector(-bounds.width * 0.5, -bounds.height * 0.5))
437 | .then_rotate(Angle::radians(self.rotation * 2. * PI))
438 | .then_translate(vector(bounds.width * 0.5, bounds.height * 0.5)),
439 | );
440 | // let _debug_frame = Path::rectangle(Point::ORIGIN, bounds.size());
441 | // frame.fill(&_debug_frame, Color::from_rgb8(255, 0, 0));
442 | frame.stroke(
443 | &backing,
444 | Stroke::default()
445 | .with_width(stroke)
446 | .with_line_cap(canvas::LineCap::Round)
447 | .with_color(self.backing_color.scale_alpha(self.opacity)),
448 | );
449 | frame.stroke(
450 | &circle,
451 | Stroke::default()
452 | .with_width(stroke)
453 | .with_line_cap(canvas::LineCap::Round)
454 | .with_color(self.spinning_color.scale_alpha(self.opacity)),
455 | );
456 | vec![frame.into_geometry()]
457 | }
458 | }
459 |
--------------------------------------------------------------------------------
/examples/iced-minimal/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "iced-minimal"
3 | version = "0.1.0"
4 | edition = "2021"
5 |
6 | [dependencies]
7 | iced = { version = "0.13.1", features = ["canvas", "tokio", "svg"] }
8 | lilt = { path = "../../" }
9 | iced_futures = "0.13.1"
10 |
--------------------------------------------------------------------------------
/examples/iced-minimal/src/main.rs:
--------------------------------------------------------------------------------
1 | use iced::widget::{horizontal_space, vertical_space, Button, Column, Row, Text};
2 | use iced::window::frames;
3 | use iced::Task;
4 | use iced::{Element, Length};
5 | use lilt::Animated;
6 | use lilt::Easing;
7 | use std::time::Instant;
8 |
9 | pub fn main() -> iced::Result {
10 | iced::application("Iced Minimal", Example::update, Example::view)
11 | .subscription(Example::subscription)
12 | .run()
13 | }
14 |
15 | struct Example {
16 | animated_toggle: Animated,
17 | }
18 |
19 | #[derive(Debug, Clone, Copy)]
20 | enum AppMessage {
21 | Animate,
22 | Tick,
23 | }
24 |
25 | impl Default for Example {
26 | fn default() -> Self {
27 | Self::new()
28 | }
29 | }
30 |
31 | impl Example {
32 | fn new() -> Self {
33 | Self {
34 | animated_toggle: Animated::new(false).duration(300.).easing(Easing::EaseOut),
35 | }
36 | }
37 |
38 | fn subscription(&self) -> iced::Subscription {
39 | let now = std::time::Instant::now();
40 | if self.animated_toggle.in_progress(now) {
41 | frames().map(|_| AppMessage::Tick)
42 | } else {
43 | iced::Subscription::none()
44 | }
45 | }
46 |
47 | fn update(&mut self, message: AppMessage) -> Task {
48 | let now = std::time::Instant::now();
49 | match message {
50 | AppMessage::Animate => self
51 | .animated_toggle
52 | .transition(!self.animated_toggle.value, now),
53 | AppMessage::Tick => (),
54 | }
55 | Task::none()
56 | }
57 |
58 | fn view(&self) -> Element {
59 | let now = std::time::Instant::now();
60 | Column::new()
61 | .align_x(iced::Alignment::Center)
62 | .push(vertical_space())
63 | .push(
64 | Button::new(
65 | Row::new()
66 | .push(horizontal_space())
67 | .push(Text::new("Animate!"))
68 | .push(horizontal_space()),
69 | )
70 | .on_press(AppMessage::Animate)
71 | .width(self.animated_toggle.animate_bool(100., 300., now)),
72 | )
73 | .push(vertical_space())
74 | .width(Length::Fill)
75 | .height(Length::Fill)
76 | .into()
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/examples/macroquad-example/Cargo.lock:
--------------------------------------------------------------------------------
1 | # This file is automatically @generated by Cargo.
2 | # It is not intended for manual editing.
3 | version = 4
4 |
5 | [[package]]
6 | name = "adler2"
7 | version = "2.0.0"
8 | source = "registry+https://github.com/rust-lang/crates.io-index"
9 | checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627"
10 |
11 | [[package]]
12 | name = "ahash"
13 | version = "0.8.11"
14 | source = "registry+https://github.com/rust-lang/crates.io-index"
15 | checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011"
16 | dependencies = [
17 | "cfg-if",
18 | "once_cell",
19 | "version_check",
20 | "zerocopy",
21 | ]
22 |
23 | [[package]]
24 | name = "autocfg"
25 | version = "1.4.0"
26 | source = "registry+https://github.com/rust-lang/crates.io-index"
27 | checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
28 |
29 | [[package]]
30 | name = "bitflags"
31 | version = "1.3.2"
32 | source = "registry+https://github.com/rust-lang/crates.io-index"
33 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
34 |
35 | [[package]]
36 | name = "bumpalo"
37 | version = "3.17.0"
38 | source = "registry+https://github.com/rust-lang/crates.io-index"
39 | checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf"
40 |
41 | [[package]]
42 | name = "bytemuck"
43 | version = "1.20.0"
44 | source = "registry+https://github.com/rust-lang/crates.io-index"
45 | checksum = "8b37c88a63ffd85d15b406896cc343916d7cf57838a847b3a6f2ca5d39a5695a"
46 |
47 | [[package]]
48 | name = "byteorder"
49 | version = "1.5.0"
50 | source = "registry+https://github.com/rust-lang/crates.io-index"
51 | checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
52 |
53 | [[package]]
54 | name = "cfg-if"
55 | version = "1.0.0"
56 | source = "registry+https://github.com/rust-lang/crates.io-index"
57 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
58 |
59 | [[package]]
60 | name = "color_quant"
61 | version = "1.1.0"
62 | source = "registry+https://github.com/rust-lang/crates.io-index"
63 | checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
64 |
65 | [[package]]
66 | name = "crc32fast"
67 | version = "1.4.2"
68 | source = "registry+https://github.com/rust-lang/crates.io-index"
69 | checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3"
70 | dependencies = [
71 | "cfg-if",
72 | ]
73 |
74 | [[package]]
75 | name = "fdeflate"
76 | version = "0.3.6"
77 | source = "registry+https://github.com/rust-lang/crates.io-index"
78 | checksum = "07c6f4c64c1d33a3111c4466f7365ebdcc37c5bd1ea0d62aae2e3d722aacbedb"
79 | dependencies = [
80 | "simd-adler32",
81 | ]
82 |
83 | [[package]]
84 | name = "flate2"
85 | version = "1.0.35"
86 | source = "registry+https://github.com/rust-lang/crates.io-index"
87 | checksum = "c936bfdafb507ebbf50b8074c54fa31c5be9a1e7e5f467dd659697041407d07c"
88 | dependencies = [
89 | "crc32fast",
90 | "miniz_oxide",
91 | ]
92 |
93 | [[package]]
94 | name = "fontdue"
95 | version = "0.7.3"
96 | source = "registry+https://github.com/rust-lang/crates.io-index"
97 | checksum = "0793f5137567643cf65ea42043a538804ff0fbf288649e2141442b602d81f9bc"
98 | dependencies = [
99 | "hashbrown",
100 | "ttf-parser",
101 | ]
102 |
103 | [[package]]
104 | name = "glam"
105 | version = "0.27.0"
106 | source = "registry+https://github.com/rust-lang/crates.io-index"
107 | checksum = "9e05e7e6723e3455f4818c7b26e855439f7546cf617ef669d1adedb8669e5cb9"
108 |
109 | [[package]]
110 | name = "hashbrown"
111 | version = "0.13.2"
112 | source = "registry+https://github.com/rust-lang/crates.io-index"
113 | checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e"
114 | dependencies = [
115 | "ahash",
116 | ]
117 |
118 | [[package]]
119 | name = "image"
120 | version = "0.24.9"
121 | source = "registry+https://github.com/rust-lang/crates.io-index"
122 | checksum = "5690139d2f55868e080017335e4b94cb7414274c74f1669c84fb5feba2c9f69d"
123 | dependencies = [
124 | "bytemuck",
125 | "byteorder",
126 | "color_quant",
127 | "num-traits",
128 | "png",
129 | ]
130 |
131 | [[package]]
132 | name = "js-sys"
133 | version = "0.3.77"
134 | source = "registry+https://github.com/rust-lang/crates.io-index"
135 | checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f"
136 | dependencies = [
137 | "once_cell",
138 | "wasm-bindgen",
139 | ]
140 |
141 | [[package]]
142 | name = "libc"
143 | version = "0.2.166"
144 | source = "registry+https://github.com/rust-lang/crates.io-index"
145 | checksum = "c2ccc108bbc0b1331bd061864e7cd823c0cab660bbe6970e66e2c0614decde36"
146 |
147 | [[package]]
148 | name = "lilt"
149 | version = "0.8.0"
150 | dependencies = [
151 | "web-time",
152 | ]
153 |
154 | [[package]]
155 | name = "log"
156 | version = "0.4.26"
157 | source = "registry+https://github.com/rust-lang/crates.io-index"
158 | checksum = "30bde2b3dc3671ae49d8e2e9f044c7c005836e7a023ee57cffa25ab82764bb9e"
159 |
160 | [[package]]
161 | name = "macroquad"
162 | version = "0.4.13"
163 | source = "registry+https://github.com/rust-lang/crates.io-index"
164 | checksum = "81fef16b2d4de22ac372b5d7d76273c0ead0a31f9de9bc649af8f816816db8a8"
165 | dependencies = [
166 | "fontdue",
167 | "glam",
168 | "image",
169 | "macroquad_macro",
170 | "miniquad",
171 | "quad-rand",
172 | "slotmap",
173 | ]
174 |
175 | [[package]]
176 | name = "macroquad-example"
177 | version = "0.1.0"
178 | dependencies = [
179 | "lilt",
180 | "macroquad",
181 | ]
182 |
183 | [[package]]
184 | name = "macroquad_macro"
185 | version = "0.1.8"
186 | source = "registry+https://github.com/rust-lang/crates.io-index"
187 | checksum = "64b1d96218903768c1ce078b657c0d5965465c95a60d2682fd97443c9d2483dd"
188 |
189 | [[package]]
190 | name = "malloc_buf"
191 | version = "0.0.6"
192 | source = "registry+https://github.com/rust-lang/crates.io-index"
193 | checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb"
194 | dependencies = [
195 | "libc",
196 | ]
197 |
198 | [[package]]
199 | name = "miniquad"
200 | version = "0.4.6"
201 | source = "registry+https://github.com/rust-lang/crates.io-index"
202 | checksum = "2124e5e6bab86d96f263d78dc763c29e0da4c4f7ff0e1bb33f1d3905d1121262"
203 | dependencies = [
204 | "libc",
205 | "ndk-sys",
206 | "objc",
207 | "winapi",
208 | ]
209 |
210 | [[package]]
211 | name = "miniz_oxide"
212 | version = "0.8.0"
213 | source = "registry+https://github.com/rust-lang/crates.io-index"
214 | checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1"
215 | dependencies = [
216 | "adler2",
217 | "simd-adler32",
218 | ]
219 |
220 | [[package]]
221 | name = "ndk-sys"
222 | version = "0.2.2"
223 | source = "registry+https://github.com/rust-lang/crates.io-index"
224 | checksum = "e1bcdd74c20ad5d95aacd60ef9ba40fdf77f767051040541df557b7a9b2a2121"
225 |
226 | [[package]]
227 | name = "num-traits"
228 | version = "0.2.19"
229 | source = "registry+https://github.com/rust-lang/crates.io-index"
230 | checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
231 | dependencies = [
232 | "autocfg",
233 | ]
234 |
235 | [[package]]
236 | name = "objc"
237 | version = "0.2.7"
238 | source = "registry+https://github.com/rust-lang/crates.io-index"
239 | checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1"
240 | dependencies = [
241 | "malloc_buf",
242 | ]
243 |
244 | [[package]]
245 | name = "once_cell"
246 | version = "1.20.2"
247 | source = "registry+https://github.com/rust-lang/crates.io-index"
248 | checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775"
249 |
250 | [[package]]
251 | name = "png"
252 | version = "0.17.14"
253 | source = "registry+https://github.com/rust-lang/crates.io-index"
254 | checksum = "52f9d46a34a05a6a57566bc2bfae066ef07585a6e3fa30fbbdff5936380623f0"
255 | dependencies = [
256 | "bitflags",
257 | "crc32fast",
258 | "fdeflate",
259 | "flate2",
260 | "miniz_oxide",
261 | ]
262 |
263 | [[package]]
264 | name = "proc-macro2"
265 | version = "1.0.92"
266 | source = "registry+https://github.com/rust-lang/crates.io-index"
267 | checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0"
268 | dependencies = [
269 | "unicode-ident",
270 | ]
271 |
272 | [[package]]
273 | name = "quad-rand"
274 | version = "0.2.3"
275 | source = "registry+https://github.com/rust-lang/crates.io-index"
276 | checksum = "5a651516ddc9168ebd67b24afd085a718be02f8858fe406591b013d101ce2f40"
277 |
278 | [[package]]
279 | name = "quote"
280 | version = "1.0.37"
281 | source = "registry+https://github.com/rust-lang/crates.io-index"
282 | checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
283 | dependencies = [
284 | "proc-macro2",
285 | ]
286 |
287 | [[package]]
288 | name = "simd-adler32"
289 | version = "0.3.7"
290 | source = "registry+https://github.com/rust-lang/crates.io-index"
291 | checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe"
292 |
293 | [[package]]
294 | name = "slotmap"
295 | version = "1.0.7"
296 | source = "registry+https://github.com/rust-lang/crates.io-index"
297 | checksum = "dbff4acf519f630b3a3ddcfaea6c06b42174d9a44bc70c620e9ed1649d58b82a"
298 | dependencies = [
299 | "version_check",
300 | ]
301 |
302 | [[package]]
303 | name = "syn"
304 | version = "2.0.89"
305 | source = "registry+https://github.com/rust-lang/crates.io-index"
306 | checksum = "44d46482f1c1c87acd84dea20c1bf5ebff4c757009ed6bf19cfd36fb10e92c4e"
307 | dependencies = [
308 | "proc-macro2",
309 | "quote",
310 | "unicode-ident",
311 | ]
312 |
313 | [[package]]
314 | name = "ttf-parser"
315 | version = "0.15.2"
316 | source = "registry+https://github.com/rust-lang/crates.io-index"
317 | checksum = "7b3e06c9b9d80ed6b745c7159c40b311ad2916abb34a49e9be2653b90db0d8dd"
318 |
319 | [[package]]
320 | name = "unicode-ident"
321 | version = "1.0.14"
322 | source = "registry+https://github.com/rust-lang/crates.io-index"
323 | checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83"
324 |
325 | [[package]]
326 | name = "version_check"
327 | version = "0.9.5"
328 | source = "registry+https://github.com/rust-lang/crates.io-index"
329 | checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
330 |
331 | [[package]]
332 | name = "wasm-bindgen"
333 | version = "0.2.100"
334 | source = "registry+https://github.com/rust-lang/crates.io-index"
335 | checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5"
336 | dependencies = [
337 | "cfg-if",
338 | "once_cell",
339 | "wasm-bindgen-macro",
340 | ]
341 |
342 | [[package]]
343 | name = "wasm-bindgen-backend"
344 | version = "0.2.100"
345 | source = "registry+https://github.com/rust-lang/crates.io-index"
346 | checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6"
347 | dependencies = [
348 | "bumpalo",
349 | "log",
350 | "proc-macro2",
351 | "quote",
352 | "syn",
353 | "wasm-bindgen-shared",
354 | ]
355 |
356 | [[package]]
357 | name = "wasm-bindgen-macro"
358 | version = "0.2.100"
359 | source = "registry+https://github.com/rust-lang/crates.io-index"
360 | checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407"
361 | dependencies = [
362 | "quote",
363 | "wasm-bindgen-macro-support",
364 | ]
365 |
366 | [[package]]
367 | name = "wasm-bindgen-macro-support"
368 | version = "0.2.100"
369 | source = "registry+https://github.com/rust-lang/crates.io-index"
370 | checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de"
371 | dependencies = [
372 | "proc-macro2",
373 | "quote",
374 | "syn",
375 | "wasm-bindgen-backend",
376 | "wasm-bindgen-shared",
377 | ]
378 |
379 | [[package]]
380 | name = "wasm-bindgen-shared"
381 | version = "0.2.100"
382 | source = "registry+https://github.com/rust-lang/crates.io-index"
383 | checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d"
384 | dependencies = [
385 | "unicode-ident",
386 | ]
387 |
388 | [[package]]
389 | name = "web-time"
390 | version = "1.1.0"
391 | source = "registry+https://github.com/rust-lang/crates.io-index"
392 | checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb"
393 | dependencies = [
394 | "js-sys",
395 | "wasm-bindgen",
396 | ]
397 |
398 | [[package]]
399 | name = "winapi"
400 | version = "0.3.9"
401 | source = "registry+https://github.com/rust-lang/crates.io-index"
402 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
403 | dependencies = [
404 | "winapi-i686-pc-windows-gnu",
405 | "winapi-x86_64-pc-windows-gnu",
406 | ]
407 |
408 | [[package]]
409 | name = "winapi-i686-pc-windows-gnu"
410 | version = "0.4.0"
411 | source = "registry+https://github.com/rust-lang/crates.io-index"
412 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
413 |
414 | [[package]]
415 | name = "winapi-x86_64-pc-windows-gnu"
416 | version = "0.4.0"
417 | source = "registry+https://github.com/rust-lang/crates.io-index"
418 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
419 |
420 | [[package]]
421 | name = "zerocopy"
422 | version = "0.7.35"
423 | source = "registry+https://github.com/rust-lang/crates.io-index"
424 | checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
425 | dependencies = [
426 | "zerocopy-derive",
427 | ]
428 |
429 | [[package]]
430 | name = "zerocopy-derive"
431 | version = "0.7.35"
432 | source = "registry+https://github.com/rust-lang/crates.io-index"
433 | checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
434 | dependencies = [
435 | "proc-macro2",
436 | "quote",
437 | "syn",
438 | ]
439 |
--------------------------------------------------------------------------------
/examples/macroquad-example/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "macroquad-example"
3 | version = "0.1.0"
4 | edition = "2021"
5 |
6 | [dependencies]
7 | lilt = { path = "../.." }
8 | macroquad = { version = "0.4.13" }
9 |
--------------------------------------------------------------------------------
/examples/macroquad-example/src/main.rs:
--------------------------------------------------------------------------------
1 | use lilt::*;
2 | use macroquad::prelude::*;
3 | use std::f32::consts::PI;
4 |
5 | #[macroquad::main("BasicShapes")]
6 | async fn main() {
7 | let time = get_time() as f32 * 1000.;
8 | let animation1: Animated = Animated::new(false)
9 | .duration(2000.)
10 | .easing(Easing::EaseInOutQuint)
11 | .repeat_forever()
12 | .auto_reverse()
13 | .auto_start(true, time);
14 |
15 | let animation2: Animated = Animated::new(false)
16 | .duration(2000.)
17 | .easing(Easing::Linear)
18 | .repeat_forever()
19 | .auto_start(true, time);
20 |
21 | loop {
22 | let time = get_time() as f32 * 1000.;
23 | clear_background(LIGHTGRAY);
24 |
25 | let rect_left = animation1.animate_bool(100., screen_width() - 200.0, time);
26 | let rotation = animation2.animate_bool(0., 2., time);
27 |
28 | let line_origin_x = 300.;
29 | let line_origin_y = 300.;
30 | let length = 400.;
31 | draw_line(
32 | line_origin_x,
33 | line_origin_y,
34 | (rotation * PI).cos() * (length - line_origin_x)
35 | - (rotation * PI).sin() * (length - line_origin_x)
36 | + line_origin_x,
37 | (rotation * PI).sin() * (length - line_origin_y)
38 | + (rotation * PI).cos() * (length - line_origin_y)
39 | + line_origin_y,
40 | 15.0,
41 | BLUE,
42 | );
43 | draw_rectangle(rect_left, 100.0, 120.0, 60.0, GREEN);
44 |
45 | next_frame().await
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/animated.rs:
--------------------------------------------------------------------------------
1 | use crate::traits::{AnimationTime, FloatRepresentable, Interpolable};
2 | /// Wraps state to enable interpolated transitions
3 | ///
4 | /// # Example
5 | ///
6 | /// ```rust
7 | /// use lilt::Animated;
8 | /// use std::time::Instant;
9 | ///
10 | /// struct MyViewState {
11 | /// animated_toggle: Animated,
12 | /// }
13 | /// // Initialize
14 | /// let mut state = MyViewState {
15 | /// animated_toggle: Animated::new(false),
16 | /// };
17 | /// // Update
18 | /// let now = std::time::Instant::now();
19 | /// state
20 | /// .animated_toggle
21 | /// .transition(!state.animated_toggle.value, now);
22 | /// // Animate
23 | /// let animated_width = state.animated_toggle.animate_bool(0., 100., now);
24 | /// let animated_width = state.animated_toggle.animate(
25 | /// |on| if on { 0. } else { 100. },
26 | /// now,
27 | /// );
28 | /// ```
29 | ///
30 | /// An `Animated` struct represents a single animation axis. Multiple axes require multiple `Animated` structs.
31 | /// For example - to animate an x and a y position on the screen with different durations you'd need to
32 | /// wrap multiple float values independently.
33 | ///
34 | /// ```rust
35 | /// use std::time::Instant;
36 | /// use lilt::Animated;
37 | ///
38 | /// struct MyState {
39 | /// animated_x: Animated,
40 | /// animated_y: Animated,
41 | /// }
42 | /// ```
43 | #[derive(Clone, Debug, Default)]
44 | pub struct Animated
45 | where
46 | T: FloatRepresentable + Clone + Copy + PartialEq,
47 | Time: AnimationTime,
48 | {
49 | animation: Animation