Skip to content

Commit

Permalink
Optimize list ripple and topbar animation
Browse files Browse the repository at this point in the history
  • Loading branch information
apqx committed Jun 14, 2024
1 parent 1a9a429 commit 44a99c4
Show file tree
Hide file tree
Showing 23 changed files with 212 additions and 118 deletions.
8 changes: 0 additions & 8 deletions _includes/aside.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,44 +7,36 @@
<div class="mdc-drawer__content">
<nav class="mdc-deprecated-list nav-list" role="listbox">
<a class="mdc-deprecated-list-item" role="option" id="drawer-a-original" href="{% link index.html %}">
<span class="mdc-deprecated-list-item__ripple"></span>
<i class="material-symbols-rounded mdc-deprecated-list-item__graphic">palette</i>
<span class="mdc-deprecated-list-item__text mdc-typography--body2">随笔</span>
</a>
<a class="mdc-deprecated-list-item" role="option" id="drawer-a-repost"
href="{% link section/repost.html %}">
<span class="mdc-deprecated-list-item__ripple"></span>
<i class="material-symbols-rounded mdc-deprecated-list-item__graphic">auto_awesome</i>
<span class="mdc-deprecated-list-item__text mdc-typography--body2">转载</span>
</a>
<a class="mdc-deprecated-list-item" role="option" id="drawer-a-poetry"
href="{% link section/poetry.html %}">
<span class="mdc-deprecated-list-item__ripple"></span>
<i class="material-symbols-rounded mdc-deprecated-list-item__graphic">translate</i>
<span class="mdc-deprecated-list-item__text mdc-typography--body2">诗文</span>
</a>
<a class="mdc-deprecated-list-item" role="option" id="drawer-a-opera" href="{% link section/opera.html %}">
<span class="mdc-deprecated-list-item__ripple"></span>
<i class="material-symbols-rounded mdc-deprecated-list-item__graphic">photo_camera</i>
<span class="mdc-deprecated-list-item__text mdc-typography--body2">看剧</span>
</a>
<a class="mdc-deprecated-list-item" role="option" id="drawer-a-tag" href="{% link section/tag.html %}">
<span class="mdc-deprecated-list-item__ripple"></span>
<i class="material-symbols-rounded mdc-deprecated-list-item__graphic">local_offer</i>
<span class="mdc-deprecated-list-item__text mdc-typography--body2">标签</span>
</a>
<a class="mdc-deprecated-list-item" role="option" id="drawer-a-search">
<span class="mdc-deprecated-list-item__ripple"></span>
<i class="material-symbols-rounded mdc-deprecated-list-item__graphic">search</i>
<span class="mdc-deprecated-list-item__text mdc-typography--body2">搜索</span>
</a>
<a class="mdc-deprecated-list-item" role="option" id="drawer-a-preference">
<span class="mdc-deprecated-list-item__ripple"></span>
<i class="material-symbols-rounded mdc-deprecated-list-item__graphic">manage_accounts</i>
<span class="mdc-deprecated-list-item__text mdc-typography--body2">偏好</span>
</a>
<a class="mdc-deprecated-list-item" role="option" id="drawer-a-about-me">
<span class="mdc-deprecated-list-item__ripple"></span>
<i class="material-symbols-rounded mdc-deprecated-list-item__graphic">face</i>
<span class="mdc-deprecated-list-item__text mdc-typography--body2">关于我</span>
</a>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,22 @@ layout: post
categories: original
title: "基于Pagefind实现静态博客站内搜索"
author: 立泉
mention: Jekyll React Async
mention: Jekyll Webpack Async
date: 2024-06-12 +0800
description: Jekyll生成的静态博客并不支持站内搜索,以往实现要么使用搜索引擎的site:mudan.me限定站点,要么搭配服务器自建全文搜索服务,但是前者索引更新缓慢,后者则太“重”,总之对于倾向免费托管的静态博客并没有特别好的方案。
cover:
tags: Code Blog Pagefind 搜索 Jekyll
tags: Code Blog Pagefind Webpack Jekyll 搜索
---

`Jekyll`生成的静态博客并不支持站内搜索,以往实现要么使用搜索引擎的`site:mudan.me`限定站点,要么搭配服务器自建全文搜索服务,但是前者索引更新缓慢,后者则太“重”,总之对于倾向免费托管的静态博客并没有特别好的方案。

直到2022年[Pagefind](https://pagefind.app/){: target="_blank" }出现,它是一个基于`Rust``WebAssembly`的轻量级静态搜索工具。先使用其`Terminal`命令扫描站点生成索引文件和对应的检索`API`,所谓“API”其实就是`Wasm`程序提供的`Javascript`函数接口,搜索时只需在`Web`中加载调用它们即可自动访问索引文件从中生成结果
直到2022年[Pagefind](https://pagefind.app/){: target="_blank" }出现,它是一个基于`Rust``WebAssembly`的轻量级静态搜索工具。先使用其`Terminal`命令扫描站点生成索引文件和对应的检索`API`,所谓“API”其实就是`Wasm`程序提供的`Javascript`函数接口,搜索时在`Web`页面加载调用它们即可自动访问索引文件从中生成结果

这种模式只是从浏览器访问云端的静态资源,并不需要通常意义上的“搜索服务器”,而且它的搜索算法也会尽量降低检索文件时传输的数据量来节省带宽。以本博客为例,每次搜索下载的数据甚至低于分段字体,所以不必担心高频访问的问题。

## 索引

生成索引需要先安装`Pagefind`命令行工具,由于是`Node.js`包,所以直接使用`npx`即可:
生成索引需要先安装`Pagefind`命令行工具,由于是`Node.js`包,可以直接使用`npx`:

```sh
# 扫描--site指定的目录,将索引输出到--output-path指定的目录下
Expand Down Expand Up @@ -68,11 +68,11 @@ npx -y pagefind --site _site --output-path npm/pagefind

至于`pagefind-modular-ui`则是模块化的组件`UI`,搜索框和搜索结果是分开的,可以自由插入到指定位置,配置更加灵活。详细信息参考[这里](https://www.npmjs.com/package/@pagefind/modular-ui){: target="_blank" }。

一般情况下,这些文件应该和博客一起托管到相同位置,但是对于考虑国内访问的海外站点,也可把它们单独放到大陆的服务器或`OSS`上,以达到更快的访问速度。
一般情况下,这些文件应该和博客主站托管到一起,但是对于考虑国内访问的海外站点,也可把它们单独放到大陆的服务器或`OSS`上,以达到更快的访问速度。

## 搜索

如果对预置`UI`不满意,可以使用`Pagefind`提供的`JS`函数接口来调用搜索`API`,自行处理搜索逻辑和结果展示。
如果对预置`UI`不满意,可以使用`Pagefind`提供的`JS`函数接口手动调用搜索`API`,自行处理搜索逻辑和结果展示。

```ts
// pagefind.js文件URL,我在部署时把它放到了国内OSS上,与主站分离
Expand All @@ -89,16 +89,22 @@ const pagefindResult = await this.pagefind.search(key)

![](https://apqx.oss-cn-hangzhou.aliyuncs.com/blog/original/20240612/pagefind-api.webp){: loading="lazy" class="clickable clickShowOriginalImg" alt="Pagefind Material Design" }

注意图中`Tips``Pagefind`为实现轻量化是对`单词`建立索引,而它的中文`分词`并不完善,一些人名之类的低频词并不能被正确识别,这个时候可以尝试在单字之间加空格,它会搜索同时包含这些字的结果,匹配准确度显著提升
注意图中`Tips``Pagefind`为实现轻量化是对`单词`建立索引,而它的中文`分词`并不完善,一些人名之类的低频词并不能被正确识别,这个时候可以尝试在`单字`之间加空格,它会搜索同时包含这些``的结果,匹配准确度更好

## Webpack
### Webpack

如上所述调用搜索`API`很简单,只需要`import`对应的`JS`文件而已,但是在`Webpack`中,`import``JS`必须是本地文件且会被直接打包而非运行时动态从指定位置加载,导致搜索`JS`总是不能正确访问到云端索引...这个莫名其妙的问题真的困扰我很久,毕竟不是`Web`开发者,明明按照文档一步步做,结果却一直卡住走不通...`Google`那一长串`Error`翻了好多页才终于找到与之相关的一篇文章
如上所述调用搜索`API`很简单,只需要`import`对应的`JS`文件而已,但是在`Webpack`中,`import``JS`必须是本地文件且会被直接打包而非运行时动态从指定位置加载,导致`Pagefind`搜索函数总是不能正确访问到云端索引...这个莫名其妙的问题困扰我很久,毕竟不是`Web`开发者,明明按照文档一步步做,结果却一直卡住走不通...之后不断试错,`Google`一长串`Error`翻好多页才终于找到与之相关的一篇文章

正确做法仅仅是添加一个`webpackIgnore`注释,告诉`Webpack`不要打包`import`的这个`JS`文件,留到运行时再加载。
正确做法仅仅是添加一个`webpackIgnore`注释,告诉`Webpack`不要打包`import`的这个`pagefind.js`文件,留到运行时再加载。

```ts
const pagefind = await import(/*webpackIgnore: true*/ pagefindUrl)
```

就是这么简单🙄。
就是这么简单🙄。

## 未完待续

对于博客,`Pagefind`提供的基础搜索已经足够,此外它也支持定制索引范围、指定索引元数据、搜索过滤、搜索排序、搜索多个源等高阶功能,如果配置得当甚至可以替代`Jekyll``Tag`标签和`Pagination`分页。

这也是我最近考虑要做的事,分页,用它还是`Jekyll`的原生功能呢。
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ layout: post
categories: repost
title: "单雯,开蓝色跑车的「最美杜丽娘」"
author: 拉拉
mention: 张继青
mention: 张继青 新浪博客
date: 2011-06-17 +0800
description: 2019年,还是二级演员的单雯拿到了中国戏剧梅花奖,在我们眼中的确来得有点晚,10年前便已是“最美杜丽娘”的她不应该等这么久。这些年,人们提到昆曲总念她,也是她把我引入昆曲的大门,让我知道这个世界上真的有如此优雅美好的舞台艺术。
cover: https://apqx.oss-cn-hangzhou.aliyuncs.com/blog/repost/20110617/shanwen_zhangjiqing_thumb.jpg
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ layout: post
categories: repost
title: "张继青亲授单雯再现经典《牡丹亭》"
author: 拉拉
mention: 杨汗如
mention: 杨汗如 新浪博客
date: 2012-07-12- +0800
description: 我在新浪博客上找到了很多省昆十年前的照片,那时候以微博为代表的移动互联网还没有崛起,人们会使用博客发长文来记录和分享,这些精心配合的文字图片不是现在碎片化的信息流可以相比的。如白驹过隙,属于博客的时代已经远去,我只是担心随着新浪博客某一天可能的突然关闭,这些博文会真的悄无声息消失在数字世界里。在那之前,且看且珍惜。
cover: https://apqx.oss-cn-hangzhou.aliyuncs.com/blog/repost/20120712/shanwen_zhangjiqing_06_thumb.jpg
Expand Down
2 changes: 1 addition & 1 deletion npm/dist/blog-index-v2.0.0.css

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion npm/dist/blog-index-v2.0.0.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion npm/dist/blog-scaffold-v2.0.0.css

Large diffs are not rendered by default.

28 changes: 14 additions & 14 deletions npm/dist/blog-scaffold-v2.0.0.js

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions npm/src/base/ResizeWidthObserver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ export class ResizeWidthObserver {
// 宽度没有变化
return
}
if (entry.contentRect.width == 0) {
// 宽度变为0是什么情况🙄
return
}
this.lastWidth = entry.contentRect.width
if (this.lastTimeout != null) {
clearTimeout(this.lastTimeout)
Expand Down
3 changes: 3 additions & 0 deletions npm/src/base/ScrollLoader.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { consoleDebug } from "../util/log"

export class ScrollLoader {
timeMsIgnore: number
shouldLoad: () => void
Expand All @@ -8,6 +10,7 @@ export class ScrollLoader {
}

onScroll(clientHeight: number, scrollY: number, scrollHeight: number) {
// consoleDebug("onScroll " + scrollY)
if (scrollHeight - scrollY - clientHeight < clientHeight) {
if (Date.now() - this.lastLoadTime < this.timeMsIgnore) return
this.shouldLoad()
Expand Down
1 change: 0 additions & 1 deletion npm/src/component/dialog/AboutMeDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,6 @@ class LinkItem extends React.Component<LinkItemProps, any> {
return (
<li>
<a className="mdc-deprecated-list-item mdc-deprecated-list-item__darken" href={this.props.link} target="_blank">
<span className="mdc-deprecated-list-item__ripple"></span>
<span className="mdc-deprecated-list-item__text">{this.props.title}</span>
</a>
{!this.props.last && <hr className="mdc-deprecated-list-divider" />}
Expand Down
1 change: 0 additions & 1 deletion npm/src/component/dialog/SearchDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,6 @@ class ResultItem extends React.Component<ResultItemProps, any> {
<div>
<a className="mdc-deprecated-list-item mdc-deprecated-list-item__darken mdc-ripple-upgraded"
href={this.props.data.url}>
<span className="mdc-deprecated-list-item__ripple"></span>
<span className="mdc-deprecated-list-item__text">
<span className="list-item__primary-text one-line">{this.props.data.title}</span>
<div className="list-item__secondary-text">
Expand Down
1 change: 0 additions & 1 deletion npm/src/component/dialog/TagDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,6 @@ class PostItem extends React.Component<PostItemProps, any> {
<li>
<a className="mdc-deprecated-list-item mdc-deprecated-list-item__darken tag-list-item mdc-ripple-upgraded"
href={this.props.data.url}>
<span className="mdc-deprecated-list-item__ripple"></span>
<span className="mdc-deprecated-list-item__text">
<span className="list-item__primary-text one-line">{this.props.data.title}</span>
<div className="list-item__secondary-text tag-list-item__secondary-container">
Expand Down
20 changes: 20 additions & 0 deletions npm/src/component/drawer.scss
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@
@use "@material/drawer/dismissible/mdc-drawer-dismissible";
@use "@material/elevation";
@use "@material/elevation/mdc-elevation";
@use "@material/ripple";
@use "@material/button";
@use "@material/button/mdc-button";
@use "@material/list";
@use "@material/list/mdc-list";
@use "sass:map";
@use "sass:color";

.mdc-drawer {
z-index: 3;
Expand Down Expand Up @@ -48,6 +50,24 @@
flex: 1;
}

.nav-list .mdc-deprecated-list-item.mdc-deprecated-list-item--selected {
background-color: color.adjust(blogTheme.$top-bar-color, $alpha: -0.9);;
}

.nav-list {
.mdc-deprecated-list-item {
@include ripple.surface;
@include ripple.radius-bounded(100%);
@include ripple.states-base-color(rgb(60, 60, 60));
@include ripple.states-opacities((hover: .05, focus: .05, press: .05));
}
}

.dark .nav-list .mdc-deprecated-list-item {
@include ripple.states-base-color(rgb(152, 152, 152));
@include ripple.states-opacities((hover: .05, focus: .05, press: .05));
}

.nav-bottom {
height: 3rem;
padding: 0 1rem 0 1rem;
Expand Down
8 changes: 4 additions & 4 deletions npm/src/component/list.scss
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@
.mdc-deprecated-list-item {
@include ripple.surface;
@include ripple.radius-bounded(100%);
// @include ripple.states-base-color(black);
// @include ripple.states-opacities((hover: .02, focus: .02, press: .02));
@include ripple.states-base-color(rgb(130, 130, 130));
@include ripple.states-opacities((hover: .05, focus: .05, press: .05));
padding: 0.5rem 1rem;
height: fit-content;
}
Expand All @@ -47,8 +47,8 @@
}

.dark .mdc-deprecated-list-item {
@include ripple.states-base-color(white);
@include ripple.states-opacities((hover: .05, focus: .05, press: .08));
@include ripple.states-base-color(rgb(130, 130, 130));
@include ripple.states-opacities((hover: .05, focus: .05, press: .05));
}

.dark .mdc-deprecated-list-item__darken {
Expand Down
62 changes: 31 additions & 31 deletions npm/src/component/react/GridIndexList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,18 +55,18 @@ export class GridIndexList extends BasePostPaginateShow<Props> {
super.componentDidMount()
consoleDebug("GridIndex componentDidMount")
const rootE = ReactDOM.findDOMNode(this) as HTMLElement
// this.heightAnimationContainer = new HeightAnimationContainer(rootE)
this.heightAnimationContainer = new HeightAnimationContainer(rootE)
if (this.props.onUpdate != null) this.props.onUpdate()
this.initScroll()
}

componentWillUnmount(): void {
// this.heightAnimationContainer.destroy()
this.heightAnimationContainer.destroy()
}

componentDidUpdate(prevProps: Readonly<Props>, prevState: Readonly<BasePostPaginateShowState>, snapshot?: any): void {
consoleDebug("GridIndex componentDidUpdate")
// this.heightAnimationContainer.update()
this.heightAnimationContainer.update()
if (this.props.onUpdate != null) this.props.onUpdate()
}

Expand All @@ -76,34 +76,34 @@ export class GridIndexList extends BasePostPaginateShow<Props> {
600: 1
};
return (
// <div className="height-animation-container">
<ul className="grid-index-ul">
<Masonry
breakpointCols={breakpointColumnsObj}
className="my-masonry-grid"
columnClassName="my-masonry-grid_column">
<IndexDescriptionItem innerHtml={this.props.pageDescriptionHtml} />
{this.state.posts.map((item: Post, index: number) =>
// TODO: 有时候jekyll生成的path和paginate生成的path不一样,导致item重新加载,这种情况并不多
!item.pin && !item.hide && <IndexItem key={item.path}
index={index}
title={item.title}
author={item.author}
actor={item.actor}
date={item.date}
path={item.path}
description={item.description}
cover={item.cover}
coverAlt={item.coverAlt} />
)}
{(this.state.loading || this.state.loadHint != null) &&
<li className="grid-index-li">
<LoadingHint loading={this.state.loading} loadHint={this.state.loadHint} onClickHint={this.loadMore} />
</li>
}
</Masonry>
</ul>
// </div>
<div className="height-animation-container">
<ul className="grid-index-ul">
<Masonry
breakpointCols={breakpointColumnsObj}
className="my-masonry-grid"
columnClassName="my-masonry-grid_column">
<IndexDescriptionItem innerHtml={this.props.pageDescriptionHtml} />
{this.state.posts.map((item: Post, index: number) =>
// TODO: 有时候jekyll生成的path和paginate生成的path不一样,导致item重新加载,这种情况并不多
!item.pin && !item.hide && <IndexItem key={item.path}
index={index}
title={item.title}
author={item.author}
actor={item.actor}
date={item.date}
path={item.path}
description={item.description}
cover={item.cover}
coverAlt={item.coverAlt} />
)}
{(this.state.loading || this.state.loadHint != null) &&
<li className="grid-index-li">
<LoadingHint loading={this.state.loading} loadHint={this.state.loadHint} onClickHint={this.loadMore} />
</li>
}
</Masonry>
</ul>
</div>
)
}
}
Expand Down
Loading

0 comments on commit 44a99c4

Please sign in to comment.