章节显示

This commit is contained in:
unanmed 2022-12-29 19:57:31 +08:00
parent e165bc9f74
commit a1a6539583
15 changed files with 326 additions and 328 deletions

View File

@ -18,7 +18,7 @@
"chart.js": "^4.0.1", "chart.js": "^4.0.1",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"lz-string": "^1.4.4", "lz-string": "^1.4.4",
"mutate-animate": "^0.1.0", "mutate-animate": "^1.0.0",
"vue": "^3.2.41" "vue": "^3.2.41"
}, },
"devDependencies": { "devDependencies": {

View File

@ -20,7 +20,7 @@ specifiers:
less: ^4.1.3 less: ^4.1.3
lodash: ^4.17.21 lodash: ^4.17.21
lz-string: ^1.4.4 lz-string: ^1.4.4
mutate-animate: ^0.1.0 mutate-animate: ^1.0.0
terser: ^5.15.1 terser: ^5.15.1
ts-node: ^10.9.1 ts-node: ^10.9.1
typescript: ^4.6.4 typescript: ^4.6.4
@ -36,7 +36,7 @@ dependencies:
chart.js: 4.0.1 chart.js: 4.0.1
lodash: 4.17.21 lodash: 4.17.21
lz-string: 1.4.4 lz-string: 1.4.4
mutate-animate: 0.1.0 mutate-animate: 1.0.0
vue: 3.2.45 vue: 3.2.45
devDependencies: devDependencies:
@ -2468,8 +2468,8 @@ packages:
resolution: {integrity: sha512-Tr1knR3d2mKvvWthlk7202rywKbiOm4rVFLsfAaSIhJ6dt9o47W4S+JMtWhd/PW9Wrdew2/S2fSvhz3E2gkfEg==} resolution: {integrity: sha512-Tr1knR3d2mKvvWthlk7202rywKbiOm4rVFLsfAaSIhJ6dt9o47W4S+JMtWhd/PW9Wrdew2/S2fSvhz3E2gkfEg==}
dev: true dev: true
/mutate-animate/0.1.0: /mutate-animate/1.0.0:
resolution: {integrity: sha512-XOdyX/PDFYZDpUpaqPeLOfdeDYXQcThWsuLUQpp3z6FbReDV23jIN0Vzv5QLUy+6CDMHEBZHfsumODrlKJva+g==} resolution: {integrity: sha512-Vt6zDunYjunQAJQ8mXTnamjbubWOM/hM0W+umvJKOvFP6klRAJPHO5R1XUKqEe2/mM9QM+aUfKb2q8Z0Ybwj/A==}
dev: false dev: false
/nan/2.17.0: /nan/2.17.0:

View File

@ -37,7 +37,7 @@ main.floors.MT0=
"请耐心等待字体加载完成,否则很多地方显示会很奇怪,大概需要十秒,过一段时间打开任意界面再关闭即可", "请耐心等待字体加载完成,否则很多地方显示会很奇怪,大概需要十秒,过一段时间打开任意界面再关闭即可",
{ {
"type": "function", "type": "function",
"function": "function(){\ncore.displayChapter(0);\n}" "function": "function(){\ncore.showChapter('序章 起源');\n}"
} }
], ],
"parallelDo": "", "parallelDo": "",

View File

@ -3547,236 +3547,6 @@ var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 = {
} }
} }
}, },
chapter: function () {
// 章节显示
var chapter = '',
description = '';
// 显示章节
this.displayChapter = function (index) {
if (core.isReplaying()) return;
var number = core.replaceNumberWithChinese(index);
// 获取第几章
chapter = '第' + number + '章';
if (index == 0) chapter = '序章';
// 获取描述
switch (index) {
case 0:
description = '起源';
break;
case 1:
description = '勇气';
break;
case 2:
description = '智慧';
break;
}
core.coreChapterAnimate(chapter, description);
};
// 替换数字大小写
this.replaceNumberWithChinese = function (number) {
if (number == 0) return '零';
if (number == 1) return '一';
if (number == 2) return '二';
if (number == 3) return '三';
if (number == 4) return '四';
if (number == 5) return '五';
if (number == 6) return '六';
if (number == 7) return '七';
if (number == 8) return '八';
if (number == 9) return '九';
if (number == 10) return '十';
};
// 核心动画运算
this.coreChapterAnimate = function (chapter, description) {
// 先建画布
if (core.isReplaying()) return;
core.createCanvas('chapter', 0, 0, 480, 480, 100);
var frame = 0,
speed = 0,
left = -480,
down = 240;
// 一秒50帧
core.lockControl();
var interval = setInterval(() => {
core.clearMap('chapter');
speed = core.hyperbolicCosine((frame - 84) * 0.05);
left += speed / 2;
// 背景
if (frame <= 110) {
core.fillRect(
'chapter',
0,
-240 - left,
480,
left + 480,
'#000000'
);
core.fillRect(
'chapter',
0,
240,
480,
left + 480,
'#000000'
);
} else {
core.fillRect('chapter', 0, 0, 480, down, '#000000');
core.fillRect(
'chapter',
0,
480 - down,
480,
down,
'#000000'
);
down -= speed / 2;
}
// 中间矩形
if (frame <= 100) {
core.fillRect(
'chapter',
0,
240 - frame / 5,
480,
frame / 2.5,
[255, 255, 255, 0.5 + frame / 200]
);
} else {
core.fillRect(
'chapter',
0,
240 - 2100 / (205 - frame),
480,
4200 / (205 - frame),
[255, 255, 255, (175 - frame) / 75]
);
}
// 上下方线
core.fillRect('chapter', left, 210, 300, 10, '#FF4D00');
core.fillRect('chapter', 180 - left, 260, 300, 10, '#2DFFFC');
core.fillRect('chapter', left + 310, 210, 10, 10, '#FF4D00');
core.fillRect('chapter', 160 - left, 260, 10, 10, '#2DFFFC');
core.fillPolygon(
'chapter',
[
[left + 330, 210],
[left + 330, 220],
[left + 340, 220]
],
'#FF4D00'
);
core.fillPolygon(
'chapter',
[
[150 - left, 260],
[140 - left, 260],
[150 - left, 270]
],
'#2DFFFC'
);
// 闪光条
for (var i = 5; i > 0; i--) {
if (frame <= 150) {
core.drawLine(
'chapter',
0,
220,
left + 320,
220,
[255, 255, 255, 0.4],
i
);
core.drawLine(
'chapter',
480,
260,
160 - left,
260,
[255, 255, 255, 0.4],
i
);
} else {
core.drawLine(
'chapter',
0,
220,
left + 320,
220,
[255, 255, 255, 0.4 - (frame - 150) / 125],
i
);
core.drawLine(
'chapter',
480,
260,
160 - left,
260,
[255, 255, 255, 0.4 - (frame - 150) / 125],
i
);
}
}
core.fillEllipse(
'chapter',
left + 320,
220,
7,
3,
0,
[255, 255, 255, 0.8]
);
core.fillEllipse(
'chapter',
left + 320,
220,
2,
10,
0,
[255, 255, 255, 0.8]
);
core.fillEllipse(
'chapter',
160 - left,
260,
7,
3,
0,
[255, 255, 255, 0.8]
);
core.fillEllipse(
'chapter',
160 - left,
260,
2,
10,
0,
[255, 255, 255, 0.8]
);
// 字
core.setTextAlign('chapter', 'center');
core.fillBoldText(
'chapter',
chapter + ' ' + description,
left + 360,
250,
'#ffffff',
'#000000',
'28px scroll'
);
if (frame >= 200) {
clearInterval(interval);
core.deleteCanvas('chapter');
core.unlockControl();
}
if (frame == 80) core.playSound('chapter.mp3');
frame++;
}, 20);
};
// 返回双曲余弦值
this.hyperbolicCosine = function (number) {
return 0.5 * (Math.pow(Math.E, number) + Math.pow(Math.E, -number));
};
},
intelligenceTree: function () { intelligenceTree: function () {
// 智慧加点 // 智慧加点
var list; var list;
@ -9046,6 +8816,8 @@ var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 = {
if (core.isReplaying()) showToolbox = true; if (core.isReplaying()) showToolbox = true;
core.plugin.showStatusBar.value = false; core.plugin.showStatusBar.value = false;
var statusItems = core.dom.status,
toolItems = core.dom.tools;
core.setFlag('hideStatusBar', true); core.setFlag('hideStatusBar', true);
core.setFlag('showToolbox', showToolbox || null); core.setFlag('showToolbox', showToolbox || null);
if ( if (
@ -9059,5 +8831,10 @@ var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 = {
core.dom.toolBar.style.display = 'none'; core.dom.toolBar.style.display = 'none';
} }
}; };
this.showChapter = function (chapter) {
core.plugin.chapterContent.value = chapter;
core.plugin.chapterShowed.value = true;
};
} }
}; };

View File

@ -408,25 +408,25 @@ p#name {
@-webkit-keyframes selector { @-webkit-keyframes selector {
0% { 0% {
opacity: 0.95;
}
50% {
opacity: 0.55; opacity: 0.55;
} }
50% {
opacity: 0.25;
}
100% { 100% {
opacity: 0.95; opacity: 0.55;
} }
} }
@keyframes selector { @keyframes selector {
0% { 0% {
opacity: 0.95;
}
50% {
opacity: 0.55; opacity: 0.55;
} }
50% {
opacity: 0.25;
}
100% { 100% {
opacity: 0.95; opacity: 0.55;
} }
} }

View File

@ -2,6 +2,7 @@
<div id="non-ui"> <div id="non-ui">
<StatusBar v-if="showStatusBar"></StatusBar> <StatusBar v-if="showStatusBar"></StatusBar>
<MarkedEnemy></MarkedEnemy> <MarkedEnemy></MarkedEnemy>
<Chapter v-if="chapterShowed" :chapter="chapterContent"></Chapter>
</div> </div>
</template> </template>
@ -10,6 +11,8 @@ import { ref } from 'vue';
import StatusBar from './ui/statusBar.vue'; import StatusBar from './ui/statusBar.vue';
import { showStatusBar } from './plugin/uiController'; import { showStatusBar } from './plugin/uiController';
import MarkedEnemy from './ui/markedEnemy.vue'; import MarkedEnemy from './ui/markedEnemy.vue';
import Chapter from './ui/chapter.vue';
import { chapterContent, chapterShowed } from './plugin/ui/chapter';
</script> </script>
<style lang="less" scoped> <style lang="less" scoped>

View File

@ -10,8 +10,8 @@
<span class="name">{{ enemy.name }}</span> <span class="name">{{ enemy.name }}</span>
<BoxAnimate <BoxAnimate
:id="enemy.id" :id="enemy.id"
:width="isMobile ? 32 : 50" :width="isMobile ? 32 : w"
:height="isMobile ? 32 : 50" :height="isMobile ? 32 : w"
style="margin: 5%" style="margin: 5%"
></BoxAnimate> ></BoxAnimate>
<div <div
@ -136,6 +136,8 @@ const emits = defineEmits<{
const core = window.core; const core = window.core;
const w = window.innerWidth * 0.032;
/** /**
* 选择这个怪物时 * 选择这个怪物时
*/ */

View File

@ -5,7 +5,8 @@
"在本塔中,状态栏与游戏画面是分开的。你可以自由拖动状态栏,也可以修改其大小。", "在本塔中,状态栏与游戏画面是分开的。你可以自由拖动状态栏,也可以修改其大小。",
"具体方法如下:点击一下状态栏之后,左上角的拖拽图标会放大,此时你可以按住它拖动状态栏。", "具体方法如下:点击一下状态栏之后,左上角的拖拽图标会放大,此时你可以按住它拖动状态栏。",
"你可以直接将鼠标放到状态栏的边框上,然后直接拖动以改变状态栏的大小。手机端可以先点击一下状态栏使边框", "你可以直接将鼠标放到状态栏的边框上,然后直接拖动以改变状态栏的大小。手机端可以先点击一下状态栏使边框",
"变宽,然后拖动。电脑端点击状态栏也可以使边框变宽。", "变宽,然后拖动。电脑端点击状态栏也可以使边框变宽。如果你想折叠状态栏,完全可以拖动状态栏的下边框,",
"然后直接拖动至上方,这时状态栏便会变成一条线,相当于折叠了状态栏",
"<br>", "<br>",
"<br>", "<br>",
"状态栏可以纵向滚动,如果你发现状态栏显示不全,可以尝试拉大状态栏,或者纵向拖动状态栏,就像网页上下滚动一样。", "状态栏可以纵向滚动,如果你发现状态栏显示不全,可以尝试拉大状态栏,或者纵向拖动状态栏,就像网页上下滚动一样。",
@ -30,8 +31,10 @@
"<br>", "<br>",
"3. 当勇士恰好踩到怪物的临界时,会进行提示", "3. 当勇士恰好踩到怪物的临界时,会进行提示",
"<br>", "<br>",
"4. 被标记的怪物会出现类似于状态栏的盒子,可以随意拖动和改变大小。你也可以选择关闭这个盒子,", "4. 当怪物零伤时,会进行提示",
"被关闭后可以通过重新标记来打开。这个盒子会显示标记的怪物的临界与伤害信息等,依然可以纵向滚动。", "<br>",
"5. 被标记的怪物会出现类似于状态栏的盒子,可以随意拖动和改变大小。你也可以选择关闭这个盒子,",
"被关闭后可以通过重新标记来打开。这个盒子会显示标记的怪物的临界与伤害信息等,与状态栏一样,可以纵向滚动。",
"<br>", "<br>",
"<br>", "<br>",
"这个功能可以用于标记boss或者较强的挡路怪当这些怪能够攻击时你可以直接收到信息不需要再时刻费心注意怪物的伤害。" "这个功能可以用于标记boss或者较强的挡路怪当这些怪能够攻击时你可以直接收到信息不需要再时刻费心注意怪物的伤害。"
@ -40,7 +43,54 @@
"book": { "book": {
"text": "怪物手册", "text": "怪物手册",
"desc": [ "desc": [
"" "本塔的怪物手册功能很多,下面一一介绍。",
"<br>",
"<br>",
"怪物手册打开的时候有一个0.6秒的动画,如果不想要可以在开头捡的系统设置里面关闭。",
"<br>",
"<br>",
"打开怪物手册后,怪物手册的布局与样板自带的类似。与样板不同的是,这里的怪物手册不再是翻页式结构。",
"这里的怪物手册是滚动式结构,你可以像浏览网页一样,用手指或鼠标上下滚动或者拖动右边的滚动条,电脑端还可以使用滚轮。",
"对于电脑端还可以使用键盘操作。上和下可以上下选择怪物左和右可以向上或向下移动5个怪物。这些操作与样板都类似。",
"<br>",
"<br>",
"点击一个怪物或者按下回车空格后,将进入怪物详细信息界面。这个界面分为多个栏,分别是特殊属性栏,详细临界栏,更多信息栏。",
"进入怪物详细信息后默认在特殊属性栏,该栏可以查看怪物的特殊属性。注意特殊属性依然可以纵向滚动。在特殊属性下方,",
"是怪物的临界表,可以粗略地查看怪物的临界信息。在下方,你可以点击详细临界信息进入详细临界栏。",
"<br>",
"<br>",
"在详细临界栏中,怪物的伤害会以可视化折线图的方式显示出来,从而你可以更为清晰地看出怪物减伤趋势。",
"除了查看怪物伤害曲线,你还可以规划宝石。每个折线图下方都有一个滑动条,你可以拖动来模拟吃宝石。",
"注意,拖动时,滑动条左边会显示当前的加攻或加防次数,这个数值指的是在勇士所在地图中需要吃的宝石数量。",
"例如当前勇士所在地图中一个宝石最低加2点攻击加攻数值为3那么勇士的攻击增加量就为6。",
"勇士增加的攻击数值也会在下方显示。当加攻次数和加防次数改变时,折线图也会变化。",
"当前状态下怪物的伤害以及减伤总量也会在下方显示。",
"<br>",
"<br>",
"在特殊属性栏,点击下方的怪物更多信息可以进入更多信息栏。此栏中,你可以查看怪物描述。但这不是这一栏的核心功能。",
"这一栏的核心功能是标记怪物。被标记的怪物会有一些非常方便的行为,这些行为可以在“标记怪物”说明中查看。"
]
},
"tools": {
"text": "道具栏与装备栏",
"desc": [
"道具栏与装备栏打开时会有一个0.6秒的动画,如果不想要可以在开头捡的系统设置里面关闭。",
"<br>",
"<br>",
"本塔的道具栏没有特别之处,这里不需要说明。主要是装备栏。",
"<br>",
"<br>",
"本塔的装备栏手机和电脑端不同,电脑端比手机端多了一个勇士属性的显示。在装备栏的装备列表栏,",
"上方有两个选择框与一个排序方式的选项。这三个可以筛选你拥有的装备并进行排序,从而让你能够更清楚地知道哪个装备更强。",
"第一个选择框可以筛选装备增加的属性,如果装备不增加选择的属性,那么会不显示。第二个选择框可以筛选增加的属性的方式,",
"有数值增加和百分比增加两种。在这个选择框右边有一个图标,这个图标可以改变武器的排序方式,有升序和降序两种,默认为升序。",
"例如你拥有两个装备分别增加10攻击和20攻击三者你分别选择了攻击数值升序那么增加10攻击的装备会排在上面",
"而增加20攻击的装备会排在下面。",
"<br>",
"<br>",
"对于电脑端,如果你想装装备,可以直接拖动装备至装备孔,也可以选中装备后再次点击。手机端暂时无法拖动装备。当选中一个装备后,",
"电脑端和手机端均会显示装备增加或减少的属性,注意有的装备可能不增加属性但是有特殊功能。对于电脑端,",
"还会直接在勇士属性栏显示增加或减少的属性。"
] ]
} }
} }

View File

@ -7,6 +7,7 @@ import utils from './plugin/utils';
import status from './plugin/ui/statusBar'; import status from './plugin/ui/statusBar';
import mark from './plugin/mark'; import mark from './plugin/mark';
import setting from './plugin/settings'; import setting from './plugin/settings';
import chapter from './plugin/ui/chapter';
window.addEventListener('load', () => { window.addEventListener('load', () => {
// 每个引入的插件都要在这里执行,否则不会被转发 // 每个引入的插件都要在这里执行,否则不会被转发
@ -18,7 +19,8 @@ window.addEventListener('load', () => {
utils(), utils(),
status(), status(),
mark(), mark(),
setting() setting(),
chapter()
]; ];
// 初始化所有插件并转发到core上 // 初始化所有插件并转发到core上

8
src/plugin/ui/chapter.ts Normal file
View File

@ -0,0 +1,8 @@
import { ref } from 'vue';
export const chapterShowed = ref(false);
export const chapterContent = ref('');
export default function init() {
return { chapterShowed, chapterContent };
}

154
src/types/plugin.d.ts vendored
View File

@ -14,7 +14,7 @@ type CanParseCss = keyof {
: never]: CSSStyleDeclaration[P]; : never]: CSSStyleDeclaration[P];
}; };
interface PluginDeclaration extends PluginUtils { interface PluginDeclaration extends PluginUtils, PluginUis, PluginUse {
/** /**
* 使core.addPop或core.plugin.addPop调用 * 使core.addPop或core.plugin.addPop调用
* @param px * @param px
@ -26,80 +26,12 @@ interface PluginDeclaration extends PluginUtils {
/** 添加变量 例所有的正在弹出的文字像这个就可以使用core.plugin.pop获取 */ /** 添加变量 例所有的正在弹出的文字像这个就可以使用core.plugin.pop获取 */
pop: any[]; pop: any[];
/** 怪物手册的怪物详细信息的初始位置 */
bookDetailPos: number;
/** 怪物手册详细信息展示的怪物 */
bookDetailEnemy: DetailedEnemy;
/** ui是否使用渐变 */
readonly transition: Ref<boolean>;
/** 手册是否打开 */
readonly bookOpened: Ref<boolean>;
/** 道具栏是否打开 */
readonly toolOpened: Ref<boolean>;
/** 装备栏是否打开 */
readonly equipOpened: Ref<boolean>;
/** 是否显示状态栏 */
readonly showStatusBar: Ref<boolean>;
/** 设置界面是否打开 */
readonly settingsOpened: Ref<boolean>;
/** ui栈 */
readonly uiStack: Ref<Component[]>;
/** 是否是移动设备 */
readonly isMobile: boolean;
/** 状态栏信息,取反后刷新状态栏 */ /** 状态栏信息,取反后刷新状态栏 */
readonly statusBarStatus: Ref<boolean>; readonly statusBarStatus: Ref<boolean>;
/** 检查标记的怪物,取反后更新显示信息 */ /** 检查标记的怪物,取反后更新显示信息 */
readonly checkMarkedStatus: Ref<boolean>; readonly checkMarkedStatus: Ref<boolean>;
/**
*
* @param ele
* @param fn x y和鼠标事件或点击事件
* @param ondown
* @param global
*/
useDrag(
ele: HTMLElement,
fn: DragFn,
ondown?: DragFn,
onUp?: (e: MouseEvent | TouchEvent) => void,
global: boolean = false
): void;
/**
*
* @param fn
*/
cancelGlobalDrag(fn: DragFn): void;
/**
*
* @param ele
* @param fn
*/
useWheel(
ele: HTMLElement,
fn: (x: number, y: number, z: number, e: WheelEvent) => void
): void;
/**
*
* @param ele
* @param fn
*/
useUp(ele: HTMLElement, fn: DragFn): void;
/** /**
* *
* @param fn * @param fn
@ -143,6 +75,90 @@ interface PluginUtils {
parseCss(css: string): Partial<Record<CanParseCss, string>>; parseCss(css: string): Partial<Record<CanParseCss, string>>;
} }
interface PluginUis {
/** 怪物手册的怪物详细信息的初始位置 */
bookDetailPos: number;
/** 怪物手册详细信息展示的怪物 */
bookDetailEnemy: DetailedEnemy;
/** ui是否使用渐变 */
readonly transition: Ref<boolean>;
/** 手册是否打开 */
readonly bookOpened: Ref<boolean>;
/** 道具栏是否打开 */
readonly toolOpened: Ref<boolean>;
/** 装备栏是否打开 */
readonly equipOpened: Ref<boolean>;
/** 是否显示状态栏 */
readonly showStatusBar: Ref<boolean>;
/** 设置界面是否打开 */
readonly settingsOpened: Ref<boolean>;
/** 是否正在进行章节显示 */
readonly chapterShowed: Ref<boolean>;
/** 章节显示的内容 */
readonly chapterContent: Ref<boolean>;
/** ui栈 */
readonly uiStack: Ref<Component[]>;
/**
*
* @param chapter
*/
showChapter(chapter: string): void;
}
interface PluginUse {
/** 是否是移动设备 */
readonly isMobile: boolean;
/**
*
* @param ele
* @param fn x y和鼠标事件或点击事件
* @param ondown
* @param global
*/
useDrag(
ele: HTMLElement,
fn: DragFn,
ondown?: DragFn,
onUp?: (e: MouseEvent | TouchEvent) => void,
global: boolean = false
): void;
/**
*
* @param fn
*/
cancelGlobalDrag(fn: DragFn): void;
/**
*
* @param ele
* @param fn
*/
useWheel(
ele: HTMLElement,
fn: (x: number, y: number, z: number, e: WheelEvent) => void
): void;
/**
*
* @param ele
* @param fn
*/
useUp(ele: HTMLElement, fn: DragFn): void;
}
type Forward<T> = { type Forward<T> = {
[K in keyof T as T[K] extends Function [K in keyof T as T[K] extends Function
? K extends `_${string}` ? K extends `_${string}`

View File

@ -227,12 +227,9 @@ onUnmounted(async () => {
justify-content: space-between; justify-content: space-between;
} }
#back {
font-size: 2em;
}
#tools { #tools {
height: 6%; height: 6%;
font-size: 3.2vh;
} }
.tools { .tools {

144
src/ui/chapter.vue Normal file
View File

@ -0,0 +1,144 @@
<template>
<div id="chapter">
<canvas id="chapter-back"></canvas>
<span id="chapter-text">{{ chapter }}</span>
</div>
</template>
<script lang="ts" setup>
import { Animation, hyper, power, sleep } from 'mutate-animate';
import { onMounted, ref } from 'vue';
import { has } from '../plugin/utils';
import { chapterShowed } from '../plugin/ui/chapter';
const props = defineProps<{
chapter: string;
}>();
let canvas: HTMLCanvasElement;
let ctx: CanvasRenderingContext2D;
let text: HTMLSpanElement;
onMounted(async () => {
canvas = document.getElementById('chapter-back') as HTMLCanvasElement;
ctx = canvas.getContext('2d')!;
text = document.getElementById('chapter-text') as HTMLSpanElement;
const ani = new Animation();
const w = window.innerWidth * devicePixelRatio;
const h = window.innerHeight * devicePixelRatio;
ctx.font = '5vh scroll';
const textWidth = ctx.measureText(props.chapter).width;
const line = h * 0.05;
ani.register('rect', 0);
ani.register('line', -10);
ani.register('lineOpacity', 1);
ani.register('rect2', h / 2);
ani.register('text', window.innerWidth + 10 + textWidth);
canvas.width = w;
canvas.height = h;
canvas.style.width = `${window.innerWidth}px`;
canvas.style.height = `${window.innerHeight}px`;
text.style.left = `${w + 10}px`;
text.style.top = `${window.innerHeight / 2 - h * 0.025}px`;
text.style.height = `${h * 0.05}px`;
text.style.width = `${textWidth}px`;
let soundPlayed = false;
let started = false;
ani.ticker.add(time => {
if (!has(time) || isNaN(time)) return;
if (!started) {
started = true;
return;
}
if (time >= 4000) chapterShowed.value = false;
if (!soundPlayed && time >= 1500) {
soundPlayed = true;
core.playSound('chapter.mp3');
}
ctx.restore();
ctx.save();
text.style.left = `${ani.value.text}px`;
ctx.fillStyle = '#000';
ctx.clearRect(0, 0, w, h);
if (time <= 2000) {
ctx.fillRect(0, h / 2, w, -ani.value.rect);
ctx.fillRect(0, h / 2, w, ani.value.rect);
} else if (time >= 2000 && time <= 3050) {
ctx.fillRect(0, 0, w, ani.value.rect2);
ctx.fillRect(0, h, w, -ani.value.rect2);
}
ctx.shadowColor = '#fff';
ctx.shadowBlur = 3;
ctx.shadowOffsetX = 0;
ctx.shadowOffsetY = 0;
ctx.lineWidth = 3;
ctx.strokeStyle = '#fff';
ctx.fillStyle = '#fff';
ctx.globalAlpha = ani.value.lineOpacity;
ctx.beginPath();
ctx.moveTo(0, h / 2 - line);
ctx.lineTo(ani.value.line, h / 2 - line);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(w, h / 2 + line);
ctx.lineTo(w - ani.value.line, h / 2 + line);
ctx.stroke();
ctx.shadowBlur = 0;
ctx.filter = 'blur(5px)';
ctx.beginPath();
ctx.arc(ani.value.line, h / 2 - line, 10, 0, Math.PI * 2);
ctx.fill();
ctx.beginPath();
ctx.arc(w - ani.value.line, h / 2 + line, 10, 0, Math.PI * 2);
ctx.fill();
});
ani.mode(hyper('tan', 'center'))
.time(3000)
.absolute()
.apply('line', w + 10)
.mode(hyper('sin', 'in'))
.time(1000)
.apply('rect', h / 2)
.mode(hyper('tan', 'center'))
.time(3000)
.apply('text', -textWidth * 2 - 10);
await sleep(2000);
ani.mode(hyper('sin', 'in')).time(1000).apply('rect2', 0);
await sleep(1000);
ani.mode(hyper('sin', 'out')).time(1000).apply('lineOpacity', 0);
await sleep(1000);
ani.ticker.destroy();
});
</script>
<style lang="less" scoped>
#chapter {
width: 100vw;
height: 100vh;
position: fixed;
left: 0;
top: 0;
user-select: none;
}
#chapter-back {
width: 100%;
height: 100%;
}
#chapter-text {
position: fixed;
font-family: 'scroll';
font-size: 5vh;
text-shadow: 0px 0px 5px #fff;
}
</style>

View File

@ -461,7 +461,7 @@ onUnmounted(() => {
width: 100%; width: 100%;
display: flex; display: flex;
flex-direction: row; flex-direction: row;
font-size: 2em; font-size: 3.2vh;
height: 5vh; height: 5vh;
justify-content: space-between; justify-content: space-between;
font-family: 'normal'; font-family: 'normal';

View File

@ -275,7 +275,7 @@ onUnmounted(() => {
display: flex; display: flex;
flex-direction: row; flex-direction: row;
font-family: 'normal'; font-family: 'normal';
font-size: 2em; font-size: 3.2vh;
height: 5vh; height: 5vh;
justify-content: space-between; justify-content: space-between;
@ -403,12 +403,11 @@ onUnmounted(() => {
#toolbox-main { #toolbox-main {
flex-direction: column-reverse; flex-direction: column-reverse;
height: 100%; height: 100%;
border-top: 1px solid #ddd4;
} }
.item-list { .item-list {
width: 100%; width: 100%;
height: 100%; height: 40vh;
} }
.divider { .divider {