样式和资产
遗憾的是,我们的 HotDog 应用程序还没有做好展示的准备--它完全没有风格!
在本章中,我们将为应用程序添加资产和样式。
Dioxus 使用 CSS 创建样式
如前所述,Dioxus 应用程序使用 HTML 和 CSS 作为核心标记和样式技术。我们在设计 Dioxus 时并没有像 Flutter 和 React-Native 那样重新发明轮子,而是在每个平台上都使用 HTML 和 CSS。
CSS 是目前最流行的样式系统,功能非常强大。例如,下面是 ebou 的截图,这是一款使用 Dioxus 构建的非常漂亮的 Mastodon 客户端。
HTML 和 CSS 功能强大,不必担心受到太多限制!
使用 * asset!()* 添加 CSS 文件
bare-bones 模板的 assets 文件夹中已经包含了一个基本的 main.css 文件。
├── Cargo.toml
├── assets
│ └── main.css
└── src
└── main.rs
要在应用程序中包含 CSS,我们可以使用 asset!()
宏。该宏可确保资产包含在最终的应用程序捆绑包中。
static CSS: Asset = asset!("/assets/main.css");
我们还需要使用 document::Stylesheet
组件将资产加载到应用程序中。该组件等同于 <link>
HTML 元素,但也能确保在服务器端渲染时预先加载 CSS。
fn App() -> Element {
rsx! {
document::Stylesheet { href: CSS }
}
}
与 Rust 的 include_str!()
宏不同,asset!()
宏实际上不会将资产内容包含在最终可执行文件中。相反,它会生成一个唯一路径,以便在运行时加载资产。这非常适合通过不同 HTTP 请求并行加载资产的网络应用。
📣
asset!()
宏生成的唯一名称不会与输入名称完全匹配。这有助于防止名称碰撞并改进缓存。
热加载
Dioxus 中的所有资产都支持热加载。请尝试编辑您应用程序的 main.css,并观察实时改变的效果。
图片资源
在 Dioxus 中,您可以通过两种方式包含图片:
- 动态使用 URL
- 静态使用
asset!()
宏。 使用 URL 添加图片时,只需在img {}
的 src 属性中填入图片即可。请注意,当应用程序处于离线状态时,基于 URL 的图片将无法下载。
rsx! {
// ...
div {
img { src: "https://images.dog.ceo/breeds/pitbull/dog-3981540_1280.jpg" }
}
}
对于静态图片,您可以使用与包含应用程序 CSS 相同的 asset!()
宏
static ICON: Asset = asset!("/assets/icon.png");
rsx! {
img { src: ICON }
}
优化
默认情况下,asset!()
宏会对 CSS、JavaScript、JSON 和图片进行轻量级优化。资产名称也会被修改为_包含内容哈希值_。
// would output main-j1238nask123.css
asset!("/assets/main.css").to_string();
通过可选的选项结构,您还可以进一步优化资产。例如,dx 可以自动将 .png 图像转换为更优化的 .avif 格式:
// outputs icon-j1238jd2.avif
asset!("/assets/icon.png", ImageAssetOptions::new().with_avif());
对于许多应用程序来说,资产优化是改善加载时间的最有效方法。作为开发人员,我们经常会忽略图片的大小,一不小心就会导致网站加载速度变慢。
请查看资产指南,了解有关 Dioxus 资产系统工作原理的更深入解释。
最终 CSS
我们可以利用 dx 的资产热加载系统和 CSS 知识来创建一个漂亮的应用程序:
这里有最终的 CSS 供参考:
/* App-wide styling */
html, body {
background-color: #0e0e0e;
color: white;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
height: 100%;
width: 100%;
overflow: hidden;
margin: 0;
}
#main {
display: flex;
flex-direction: column;
height: 100%;
justify-content: space-between;
}
#dogview {
max-height: 80vh;
flex-grow: 1;
width: 100%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
#dogview img {
display: block;
max-width: 50%;
max-height: 50%;
transform: scale(1.8);
border-radius: 5px;
border: 1px solid rgb(233, 233, 233);
box-shadow: 0px 0px 5px 1px rgb(216, 216, 216, 0.5);
}
#title {
text-align: center;
padding-top: 10px;
border-bottom: 1px solid #a8a8a8;
display: flex;
flex-direction: row;
justify-content: space-evenly;
align-items: center;
}
#title a {
text-decoration: none;
color: white;
}
a#heart {
background-color: white;
color: red;
padding: 5px;
border-radius: 5px;
}
#title span {
width: 20px;
}
#title h1 {
margin: 0.25em;
font-style: italic;
}
#buttons {
display: flex;
flex-direction: row;
justify-content: center;
gap: 20px;
/* padding-top: 20px; */
padding-bottom: 20px;
}
#skip { background-color: gray }
#save { background-color: green; }
#skip, #save {
padding: 5px 30px 5px 30px;
border-radius: 3px;
font-size: 2rem;
font-weight: bold;
color: rgb(230, 230, 230)
}
#navbar {
border: 1px solid rgb(233, 233, 233);
border-width: 1px 0px 0px 0px;
display: flex;
flex-direction: row;
justify-content: space-evenly;
padding: 20px;
gap: 20px;
}
#navbar a {
background-color: #a8a8a8;
border-radius: 5px;
border: 1px solid black;
text-decoration: none;
color: black;
padding: 10px 30px 10px 30px;
}
#favorites {
flex-grow: 1;
overflow: hidden;
display: flex;
flex-direction: column;
padding: 10px;
}
#favorites-container {
overflow-y: auto;
overflow-x: hidden;
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: center;
gap: 10px;
padding: 10px;
}
.favorite-dog {
max-height: 180px;
max-width: 60%;
position: relative;
}
.favorite-dog img {
max-height: 150px;
border-radius: 5px;
margin: 5px;
}
.favorite-dog:hover button {
display: block;
}
.favorite-dog button {
display: none;
position: absolute;
bottom: 10px;
left: 10px;
z-index: 10;
}