JavaScript must be enabled to play.
Browser lacks capabilities required to play.
Upgrade or switch to another browser.
Loading…
病房。深夜。窗外的光很淡。 你睁开眼。 天花板是白的。白得没有一丝温度。 你不知道自己为什么在这里。 你试着想——想自己的名字。 <span style="color:#d0d0dc">……想不起来。</span> [[我是谁?->序章-记忆]]
<<set $pw = "">> <<set $memories = 0>> <<run var css = [ "html, body { background: #0a0a0c !important; color: #c8c8d0 !important; font-family: 'Microsoft YaHei', sans-serif; font-size: 15px; line-height: 1.9; margin: 0; padding: 0; min-height: 100vh; }", "#passages { width: 100%; min-height: 100vh; margin: 0; padding: 60px 40px 120px; box-sizing: border-box; }", "a.link-internal, a.link-internal:visited { color: #7aa2c8 !important; text-decoration: none; }", "a.link-internal:hover { color: #a8c8e8 !important; }", ".game-screen { display: none; position: fixed; top: 0; left: 0; right: 0; bottom: 0; align-items: center; justify-content: center; }", ".game-screen.active { display: flex; animation: screenIn .7s cubic-bezier(.2,.7,.3,1); }", ".game-screen.fade-out { animation: screenOut .45s ease forwards; }", "@keyframes screenIn { from { opacity: 0; filter: blur(4px); } to { opacity: 1; filter: blur(0); } }", "@keyframes screenOut { from { opacity: 1; } to { opacity: 0; filter: blur(4px); } }", ".login-panel { width: 340px; padding: 40px 30px; background: #17171c; border: 1px solid #2a2a35; border-radius: 12px; text-align: center; }", ".login-logo { font-size: 48px; margin-bottom: 12px; }", ".login-title { color: #d0d0d8; font-size: 22px; letter-spacing: .2em; margin-bottom: 6px; }", ".login-sub { color: #55555f; font-size: 12px; margin-bottom: 24px; }", ".login-hint { color: #66666f; font-size: 12px; margin-bottom: 18px; }", ".login-input { width: 100%; box-sizing: border-box; background: #0d0d12; border: 1px solid #2a2a35; border-radius: 6px; color: #12b7f5; font-size: 18px; padding: 12px 16px; letter-spacing: .4em; text-align: center; outline: none; font-family: Consolas, monospace; }", ".login-input:focus { border-color: #12b7f5; }", ".login-btn { width: 100%; margin-top: 16px; background: linear-gradient(180deg, #12b7f5, #0a8cc8); border: none; color: #fff; font-size: 15px; padding: 12px; border-radius: 6px; cursor: pointer; letter-spacing: .3em; font-family: inherit; }", ".login-err { color: #d08a8a; font-size: 13px; margin-top: 12px; min-height: 1.2em; }", ".loading-panel { width: 340px; text-align: center; }", ".loading-logo { font-size: 56px; margin-bottom: 24px; animation: bob 1.5s infinite ease-in-out; }", "@keyframes bob { 0%,100% { transform: translateY(0); } 50% { transform: translateY(-8px); } }", ".loading-text { color: #b8b8c8; font-size: 14px; letter-spacing: .15em; margin-bottom: 20px; }", ".loading-bar { width: 100%; height: 4px; background: #1a1a24; border-radius: 2px; overflow: hidden; }", ".loading-fill { width: 0%; height: 100%; background: linear-gradient(90deg, #12b7f5, #4ad8ff); transition: width .6s ease; }", ".loading-tip { color: #55555f; font-size: 12px; margin-top: 16px; }", ".qq-window { width: 1060px; height: 660px; background: #1a1a20; border: 1px solid #2a2a35; border-radius: 8px; overflow: hidden; display: flex; box-shadow: 0 30px 80px rgba(0,0,0,0.7); }", ".qq-navbar { width: 60px; background: #14141a; border-right: 1px solid #0d0d12; display: flex; flex-direction: column; align-items: center; padding: 14px 0; gap: 4px; flex-shrink: 0; }", ".qq-nav-avatar { width: 36px; height: 36px; border-radius: 50%; background: #2a2a38; color: #aaa; display: flex; align-items: center; justify-content: center; font-size: 13px; margin-bottom: 14px; background-size: cover; background-position: center; position: relative; }", ".qq-nav-dot { position: absolute; top: -3px; right: -6px; background: #f04a4a; color: #fff; font-size: 9px; padding: 1px 5px; border-radius: 8px; }", ".qq-nav-item { width: 40px; height: 40px; border-radius: 6px; display: flex; align-items: center; justify-content: center; color: #66666f; font-size: 18px; cursor: pointer; flex-shrink: 0; }", ".qq-nav-item:hover { background: #1f1f28; color: #b8b8c0; }", ".qq-nav-item.active { color: #12b7f5; }", ".qq-sidebar { width: 250px; background: #17171c; border-right: 1px solid #0d0d12; display: flex; flex-direction: column; flex-shrink: 0; }", ".qq-me { height: 66px; box-sizing: border-box; padding: 0 14px; display: flex; align-items: center; gap: 10px; flex-shrink: 0; }", ".qq-me-avatar { width: 44px; height: 44px; border-radius: 50%; background: #3a5a88; color: #fff; display: flex; align-items: center; justify-content: center; font-size: 15px; flex-shrink: 0; background-size: cover; background-position: center; }", ".qq-me-info { flex: 1; min-width: 0; }", ".qq-me-name { color: #e8e8f0; font-size: 15px; font-weight: 500; }", ".qq-me-sign { color: #66666f; font-size: 11px; margin-top: 3px; }", ".qq-search-wrap { margin: 0 14px 10px; position: relative; height: 32px; flex-shrink: 0; }", ".qq-search-wrap::before { content: '🔍'; position: absolute; left: 10px; top: 50%; transform: translateY(-50%); font-size: 12px; opacity: .4; pointer-events: none; }", ".qq-search { width: 100%; height: 32px; box-sizing: border-box; padding: 0 10px 0 30px; background: #0f0f14; border: 1px solid #22222b; border-radius: 4px; color: #c8c8d0; font-size: 12px; outline: none; font-family: inherit; }", ".qq-search::placeholder { color: #44444f; }", ".qq-search:focus { border-color: #12b7f5; }", ".qq-list { flex: 1; overflow-y: auto; overflow-x: hidden; padding-top: 4px; }", ".qq-list::-webkit-scrollbar { width: 6px; }", ".qq-list::-webkit-scrollbar-track { background: transparent; }", ".qq-list::-webkit-scrollbar-thumb { background: #2a2a35; border-radius: 3px; }", ".qq-list::-webkit-scrollbar-thumb:hover { background: #3a3a48; }", ".qq-friend { height: 56px; box-sizing: border-box; padding: 0 14px; display: flex; align-items: center; gap: 10px; cursor: pointer; position: relative; transition: background .15s; }", ".qq-friend:hover { background: #1f1f28; }", ".qq-friend.active { background: #2a3a4a; }", ".qq-friend.active::before { content: ''; position: absolute; left: 0; top: 0; bottom: 0; width: 3px; background: #12b7f5; }", ".qq-friend-avatar { width: 40px; height: 40px; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-size: 14px; color: #fff; flex-shrink: 0; position: relative; background: #2a2a38; background-size: cover; background-position: center; }", ".qq-friend-avatar.gray { color: #666; }", ".qq-friend-avatar .status-dot { position: absolute; bottom: 0; right: 0; width: 10px; height: 10px; border-radius: 50%; border: 2px solid #17171c; }", ".qq-friend-avatar .status-dot.online { background: #4ade80; }", ".qq-friend-avatar .status-dot.offline { background: #555; }", ".qq-friend-info { flex: 1; min-width: 0; }", ".qq-friend-name { color: #d0d0d8; font-size: 14px; }", ".qq-friend-last { color: #55555f; font-size: 12px; margin-top: 2px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }", ".qq-friend-time { color: #44444f; font-size: 11px; flex-shrink: 0; }", ".qq-chat { flex: 1; min-width: 0; background: #1a1a20; display: flex; flex-direction: column; }", ".qq-chat-header { height: 56px; padding: 0 20px; display: flex; align-items: center; justify-content: space-between; border-bottom: 1px solid #101015; flex-shrink: 0; }", ".qq-chat-name { color: #e8e8f0; font-size: 16px; font-weight: 500; }", ".qq-chat-status { color: #66666f; font-size: 12px; margin-left: 10px; }", ".qq-chat-tools { display: flex; gap: 18px; color: #888; font-size: 17px; }", ".qq-chat-tools span { cursor: default; }", ".qq-chat-tools span:hover { color: #12b7f5; }", ".qq-chat-body { flex: 1; padding: 20px 26px; overflow-y: auto; overflow-x: hidden; }", ".qq-chat-body::-webkit-scrollbar { width: 6px; }", ".qq-chat-body::-webkit-scrollbar-track { background: transparent; }", ".qq-chat-body::-webkit-scrollbar-thumb { background: #2a2a35; border-radius: 3px; }", ".qq-chat-body::-webkit-scrollbar-thumb:hover { background: #3a3a48; }", ".qq-divider { text-align: center; margin: 16px 0; position: relative; }", ".qq-divider span { background: #1a1a20; color: #4a4a55; font-size: 11px; padding: 0 12px; position: relative; z-index: 1; }", ".qq-divider::before { content: ''; position: absolute; top: 50%; left: 0; right: 0; height: 1px; background: #26262e; }", ".qq-msg-row { display: flex; margin: 14px 0; align-items: flex-start; gap: 12px; opacity: 0; animation: msgIn .45s cubic-bezier(.2,.7,.3,1) forwards; }", ".qq-msg-row.me { flex-direction: row-reverse; }", "@keyframes msgIn { from { opacity: 0; transform: translateY(10px) scale(.96); } to { opacity: 1; transform: translateY(0) scale(1); } }", ".qq-msg-avatar { width: 40px; height: 40px; border-radius: 50%; flex-shrink: 0; display: flex; align-items: center; justify-content: center; font-size: 14px; color: #fff; background-size: cover; background-position: center; }", ".qq-msg-row.other .qq-msg-avatar { background-color: #2a5a98; }", ".qq-msg-row.me .qq-msg-avatar { background-color: #3a5a88; }", ".qq-msg-content { max-width: 60%; }", ".qq-msg-bubble { padding: 10px 15px; border-radius: 6px; font-size: 14px; line-height: 1.6; word-break: break-word; display: inline-block; }", ".qq-msg-row.other .qq-msg-bubble { background: #2a2a35; color: #e0e0e8; }", ".qq-msg-row.me .qq-msg-bubble { background: #12b7f5; color: #fff; }", ".qq-empty { color: #4a4a55; font-size: 13px; text-align: center; padding: 80px 0; }", ".qq-chat-footer { border-top: 1px solid #101015; padding: 10px 16px 14px; flex-shrink: 0; }", ".qq-searchbar { height: 56px; padding: 0 20px; display: flex; align-items: center; gap: 10px; border-bottom: 1px solid #101015; flex-shrink: 0; }", ".qq-searchbar-icon { color: #66666f; font-size: 16px; }", ".qq-searchbar-input { flex: 1; background: transparent; border: none; outline: none; color: #e0e0e8; font-size: 15px; font-family: inherit; padding: 8px 0; }", ".qq-searchbar-input::placeholder { color: #44444f; }", ".qq-searchresult { flex: 1; padding: 12px 0; overflow-y: auto; }", ".qq-searchresult-title { color: #66666f; font-size: 12px; padding: 8px 20px; letter-spacing: .1em; }", ".qq-searchrow { display: flex; align-items: center; gap: 12px; padding: 10px 20px; cursor: pointer; transition: background .15s; }", ".qq-searchrow:hover { background: #1f1f28; }", ".qq-searchrow-avatar { width: 36px; height: 36px; border-radius: 50%; background: #2a2a38; color: #aaa; display: flex; align-items: center; justify-content: center; font-size: 14px; flex-shrink: 0; }", ".qq-searchrow-info { flex: 1; min-width: 0; }", ".qq-searchrow-name { color: #d0d0d8; font-size: 14px; }", ".qq-searchrow-desc { color: #55555f; font-size: 12px; margin-top: 2px; }", ".qq-searchresult-empty { color: #55555f; font-size: 13px; text-align: center; padding: 60px 0; }", ".qq-toolbar { display: flex; gap: 20px; color: #888; font-size: 16px; padding: 4px 0 10px; }", ".qq-toolbar span { cursor: default; }", ".qq-toolbar span:hover { color: #12b7f5; }", ".qq-input-row { display: flex; gap: 10px; align-items: flex-end; }", ".qq-chat-input { flex: 1; background: transparent; border: none; color: #e0e0e8; font-size: 14px; outline: none; font-family: inherit; padding: 6px 0; }", ".qq-chat-input::placeholder { color: #44444f; }", ".qq-chat-send { background: #12b7f5; border: none; color: #fff; padding: 8px 24px; border-radius: 4px; font-size: 13px; cursor: pointer; opacity: .4; font-family: inherit; }", ".qq-chat-send:disabled { cursor: not-allowed; }", ".screen { background: #14141a; border: 1px solid #2a2a35; border-radius: 8px; padding: 20px 24px; margin: 20px 0; font-family: Consolas, monospace; font-size: 14px; line-height: 1.9; color: #7fc8a8; }", ".note { background: #14141a; border-left: 3px solid #4a4a5a; padding: 10px 16px; color: #999; font-size: 14px; margin: 18px 0; }", "button { background: linear-gradient(180deg, #2a2a35, #23232e); border: 1px solid #2a2a35; color: #a8c8e8; font-family: inherit; font-size: 14px; padding: 9px 22px; border-radius: 4px; margin-left: 10px; cursor: pointer; }", "button:hover { border-color: #12b7f5; color: #d8e4f0; }", ".sort-item { background: #23232e; border: 1px solid #2a2a35; border-radius: 6px; padding: 12px 18px; margin: 8px 0; cursor: pointer; color: #b8b8c0; font-size: 14px; }", ".sort-item:hover { border-color: #12b7f5; }", ".sort-item.placed { background: #1a2a20; border-color: #2a4a35; color: #7fc8a8; cursor: default; }", ".sort-slot { background: #0f0f15; border: 1px dashed #2a2a35; border-radius: 6px; padding: 12px 18px; margin: 8px 0; color: #44444f; font-size: 14px; min-height: 18px; }", ".sort-slot.placed { border-style: solid; border-color: #2a4a35; background: #1a2a20; color: #7fc8a8; }", ".seat-grid { display: grid; grid-template-columns: repeat(6, 1fr); gap: 6px; background: #17171c; border: 1px solid #2a2a35; border-radius: 10px; padding: 16px; margin: 24px 0; }", ".seat { background: #23232e; border: 1px solid #2a2a35; border-radius: 4px; padding: 14px 4px; text-align: center; font-size: 12px; color: #888; display: flex; align-items: center; justify-content: center; min-height: 18px; }", ".seat.named { color: #b8b8c0; }", ".seat.empty { cursor: pointer; border-style: dashed; color: #55555f; }", ".seat.empty:hover { border-color: #12b7f5; color: #12b7f5; }", ".seat.me { background: #1a3a5a; border-color: #3a6a9a; color: #a8d0f0; }", ".seat.right { background: #1a3a28; border-color: #3a8a5a; color: #7fe8a8; }", ".seat.wrong { background: #2a0f0f; border-color: #5a2a2a; color: #d08a8a; }", ".choice-item { background: #23232e; border: 1px solid #2a2a35; border-radius: 8px; padding: 14px 20px; margin: 10px 0; cursor: pointer; color: #b8b8c0; font-size: 15px; }", ".choice-item:hover { border-color: #12b7f5; }", ".choice-item.checked { background: #1a2a20; border-color: #2a4a35; color: #7fc8a8; cursor: default; }", ".choice-item.checked::before { content: '✓ '; color: #7fc8a8; font-weight: bold; }", ".photo-grid { display: grid; grid-template-columns: repeat(6, 1fr); gap: 8px; background: #17171c; border: 1px solid #2a2a35; padding: 20px; margin: 24px 0; border-radius: 10px; }", ".photo-person { background: #23232e; border-radius: 4px; padding: 14px 4px; text-align: center; font-size: 12px; color: #66666f; cursor: pointer; }", ".photo-person:hover { background: #2a2a38; color: #b8b8c0; }", ".photo-person.found { background: #fff; color: #0a0a0c; }", ".memo-paper { background: #17171c; border: 1px solid #2a2a35; border-radius: 10px; padding: 34px 28px; margin: 26px 0; color: #c8c8d0; font-size: 15px; line-height: 2.4; font-family: 'KaiTi', serif; position: relative; }", ".memo-paper::before { content: '备忘录 · 未发送'; position: absolute; top: -12px; left: 22px; background: #14141a; padding: 0 12px; color: #55555f; font-size: 12px; letter-spacing: .2em; }", ".action-btn { display: inline-block; background: #23232e; border: 1px solid #2a2a35; color: #a8d0f0; padding: 11px 26px; border-radius: 6px; margin: 18px 12px 0 0; cursor: pointer; font-family: inherit; font-size: 14px; }", ".action-btn:hover { border-color: #12b7f5; color: #d8ecff; }", ".action-btn.danger { color: #d08a8a; }", ".ending-screen { background: #0f0f18; border: 1px solid #1c1c26; border-radius: 14px; padding: 70px 26px; margin: 40px 0; text-align: center; }", ".ending-title { color: #d0d0dc; font-size: 18px; letter-spacing: .5em; margin-bottom: 44px; }", ".ending-text { color: #7fc8a8; font-size: 17px; letter-spacing: .35em; font-family: 'KaiTi', serif; }", ".fragment { background: #0f1a14; border: 1px solid #2a4a35; border-radius: 8px; padding: 14px 20px; color: #7fc8a8; font-size: 14px; letter-spacing: .2em; margin: 28px 0; text-align: center; }", ".avatar { display: inline-block; width: 40px; height: 40px; border-radius: 6px; background: #2a2a32; color: #55555f; font-size: 17px; line-height: 40px; text-align: center; vertical-align: middle; margin-right: 10px; filter: grayscale(1) brightness(0.7); }", ".avatar.online { filter: grayscale(0) brightness(1); background: #1a2a20; color: #7fc8a8; box-shadow: 0 0 18px rgba(120,220,160,0.55); }", ".click-hint { position:fixed; bottom:28px; left:50%; transform:translateX(-50%); color:#44444f; font-size:12px; letter-spacing:.3em; opacity:0; transition:opacity 1.2s; pointer-events:none; }", ".click-hint.show { opacity:1; }", "" ].join('\n'); var style = document.createElement('style'); style.textContent = css; document.head.appendChild(style); >>
你躺在床上,盯着天花板。 脑子里不是空的。有很多东西,一闪而过,像坏掉的幻灯片。 床头的病历卡翻开着。 <div class="screen"> 患者:庄大同<br> 诊断:<span style="color:#d08a8a">选择性遗忘</span><br> 建议:避免强刺激,逐步回忆 </div> <span style="color:#d0d0dc">庄大同。</span> 你在心里念了一遍这个名字。 像是在念一个陌生人。 [[坐起来->序章-电脑]]
你坐了起来。 床边的小桌上,放着一台旧笔记本电脑。 它不该亮。但它亮着。 屏幕泛着冷光。风扇在响,很轻,一高一低,<span style="color:#d0d0dc">像有人在呼吸。</span> 你打开电脑。屏幕上是一个密码输入框。下面贴着一张褪色的便签: <div class="note">六年二班 · 07</div> [[开始输入密码->序章-登录]]
六年二班。一间教室。 07。一个座位号。 06 02 0 7…… 你在心里念了一遍。 <span style="color:#d0d0dc">6207。</span> [[回去输入密码->序章-电脑]]
<div id="game-root"> <div id="screen-login" class="game-screen active"> <div class="login-panel"> <div class="login-logo">🐧</div> <div class="login-title">QQ 登录</div> <div class="login-sub">庄大同 · 六年二班</div> <div class="login-hint">提示:桌角贴纸 · 六年二班 07</div> <input type="text" id="pw-input" class="login-input" placeholder="请输入密码" autocomplete="off"> <button id="login-btn" class="login-btn">登 录</button> <div id="login-err" class="login-err"></div> </div> </div> <div id="screen-loading" class="game-screen"> <div class="loading-panel"> <div class="loading-logo">🐧</div> <div class="loading-text" id="loading-text">正在登录…</div> <div class="loading-bar"><div class="loading-fill" id="loading-fill"></div></div> <div class="loading-tip" id="loading-tip">密码正确</div> </div> </div> <div id="screen-qq" class="game-screen"> <div class="qq-window"> <div class="qq-navbar"> <div class="qq-nav-avatar" data-avatar="zhuang"><span class="qq-nav-dot">99+</span></div> <div class="qq-nav-item">💬</div> <div class="qq-nav-item active">👤</div> <div class="qq-nav-item">⭐</div> <div class="qq-nav-item">📁</div> <div class="qq-nav-item">📷</div> <div class="qq-nav-item">🎵</div> </div> <div class="qq-sidebar"> <div class="qq-me"> <div class="qq-me-avatar" data-avatar="zhuang"></div> <div class="qq-me-info"> <div class="qq-me-name">庄大同</div> <div class="qq-me-sign">[离线] 想不起来自己是谁</div> </div> </div> <div class="qq-search-wrap"> <input type="text" class="qq-search" placeholder="搜索"> </div> <div class="qq-list" id="friend-list"> <div class="qq-friend" data-friend="mama"> <div class="qq-friend-avatar gray">妈</div> <div class="qq-friend-info"> <div class="qq-friend-name">妈妈</div> <div class="qq-friend-last">[离线]</div> </div> </div> <div class="qq-friend" data-friend="yang"> <div class="qq-friend-avatar gray" data-avatar="yang"></div> <div class="qq-friend-info"> <div class="qq-friend-name">杨小语</div> <div class="qq-friend-last">他们说你喜欢我</div> </div> </div> <div class="qq-friend" data-friend="liuyixuan"> <div class="qq-friend-avatar gray" data-avatar="liuyixuan"></div> <div class="qq-friend-info"> <div class="qq-friend-name">刘亦轩</div> <div class="qq-friend-last">[离线]</div> </div> </div> <div class="qq-friend" data-friend="banzhuren"> <div class="qq-friend-avatar gray" data-avatar="banzhuren"></div> <div class="qq-friend-info"> <div class="qq-friend-name">班主任</div> <div class="qq-friend-last">[离线]</div> </div> </div> <div class="qq-friend" data-friend="suncheng"> <div class="qq-friend-avatar gray" data-avatar="suncheng"></div> <div class="qq-friend-info"> <div class="qq-friend-name">孙程</div> <div class="qq-friend-last">[离线]</div> </div> </div> <div class="qq-friend" data-friend="zhoujielun"> <div class="qq-friend-avatar gray" data-avatar="zhoujielun"></div> <div class="qq-friend-info"> <div class="qq-friend-name">周杰伦</div> <div class="qq-friend-last">[离线]</div> </div> </div> <div class="qq-friend" data-friend="wangyabo"> <div class="qq-friend-avatar gray" data-avatar="wangyabo"></div> <div class="qq-friend-info"> <div class="qq-friend-name">王亚博</div> <div class="qq-friend-last">[离线]</div> </div> </div> <div class="qq-friend" data-friend="baba"> <div class="qq-friend-avatar gray">爸</div> <div class="qq-friend-info"> <div class="qq-friend-name">爸爸</div> <div class="qq-friend-last">[离线]</div> </div> </div> </div> </div> <div class="qq-chat"> <div class="qq-chat-header"> <div> <span class="qq-chat-name" id="chat-name">点击左侧好友</span> <span class="qq-chat-status" id="chat-status"></span> </div> <div class="qq-chat-tools"> <span>📞</span><span>📹</span><span>📁</span><span>⋯</span> </div> </div> <div class="qq-chat-body" id="chat-body"> <div class="qq-empty">点击左侧好友,查看聊天记录</div> </div> <div class="qq-chat-footer"> <div class="qq-toolbar"> <span>😊</span><span>✂</span><span>📁</span><span>📷</span><span>🎤</span> </div> <div class="qq-input-row"> <input type="text" class="qq-chat-input" id="chat-input" placeholder="输入消息…" disabled> <button class="qq-chat-send" id="send-btn" disabled>发送</button> </div> </div> </div> </div> </div> </div> <script> $(function(){ var PASSWORD = "6207"; var yangSent = false; var currentFriend = ""; var AV = window.AV || {}; // 给某个元素应用头像背景 function applyAvatar($el, key){ if (!AV[key]) return; $el.css({ "background-image": "url('" + AV[key] + "')", "background-size": "cover", "background-position": "center", "background-repeat": "no-repeat" }); } // 静态头像(导航栏、我、好友列表) $("[data-avatar]").each(function(){ applyAvatar($(this), $(this).data("avatar")); }); var chats = { mama: { name: "妈妈", status: "[离线]", messages: [ {from:"other", text:"回家吃饭吗"}, {from:"me", text:"嗯"}, {from:"other", text:"早点回来"} ]}, baba: { name: "爸爸", status: "[离线]", messages: [ {from:"other", text:"作业写完了吗"} ]}, banzhuren: { name: "班主任", status: "[离线]", messages: [ {from:"other", text:"明天家长会记得来"} ]}, suncheng: { name: "孙程", status: "[离线]", messages: [ {from:"other", text:"在吗"}, {from:"me", text:"嗯"}, {from:"other", text:"打球不"} ]}, zhoujielun: { name: "周杰伦", status: "[离线]", messages: [ {from:"other", text:"明天记得带作业"} ]}, wangyabo: { name: "王亚博", status: "[离线]", messages: [ {from:"other", text:"你抄一下我的笔记吧"} ]}, liuyixuan: { name: "刘亦轩", status: "[离线]", messages: [ {from:"other", text:"作业写完了吗"}, {from:"me", text:"没有"}, {from:"other", text:"借我抄抄"} ]}, yang: { name: "杨小语", status: "[离线]", special: true, messages: [ {divider:"2019-06-XX 23:47"}, {from:"other", text:"他们说你喜欢我"} ]} }; // 生成消息头像 HTML(优先用图片,无图则文字) function avatarHTML(key, fallbackChar){ if (AV[key]) { return '<div class="qq-msg-avatar" style="background-image:url(\'' + AV[key] + '\');background-size:cover;background-position:center;color:transparent;"></div>'; } return '<div class="qq-msg-avatar">' + fallbackChar + '</div>'; } function switchScreen(fromId, toId, callback){ $("#" + fromId).addClass("fade-out"); setTimeout(function(){ $("#" + fromId).removeClass("active fade-out"); $("#" + toId).addClass("active"); if (callback) callback(); }, 450); } $("#login-btn").on("click", function(){ var pw = $("#pw-input").val().trim(); if (pw === PASSWORD) { switchScreen("screen-login", "screen-loading", startLoading); } else { $("#login-err").text("密码错误。"); } }); $("#pw-input").on("keypress", function(e){ if (e.which === 13) $("#login-btn").click(); }); function startLoading(){ var steps = [ {t:"正在验证身份…", p:20}, {t:"正在加载好友列表…", p:50}, {t:"正在同步聊天记录…", p:80}, {t:"登录成功", p:100} ]; var i = 0; function next(){ if (i < steps.length) { var step = steps[i]; $("#loading-text").text(step.t); $("#loading-fill").css("width", step.p + "%"); i++; setTimeout(next, 750); } else { setTimeout(function(){ switchScreen("screen-loading", "screen-qq"); }, 500); } } next(); } function loadChat(id){ var chat = chats[id]; if (!chat) return; currentFriend = id; $("#chat-name").text(chat.name); $("#chat-status").text(chat.status); var $body = $("#chat-body").empty(); var i = 0; function addNext(){ if (i >= chat.messages.length) { if (chat.special && !yangSent) { $("#chat-input").prop("disabled", false).attr("placeholder", "输入消息…"); $("#send-btn").prop("disabled", true).css("opacity", 0.4); } else if (chat.special && yangSent) { $("#chat-input").prop("disabled", true); } else { $("#chat-input").prop("disabled", false).attr("placeholder", "输入消息…"); $("#send-btn").prop("disabled", true).css("opacity", 0.4); } return; } var m = chat.messages[i]; var html = ""; if (m.divider) { html = '<div class="qq-divider"><span>' + m.divider + '</span></div>'; } else if (m.from === "other") { html = '<div class="qq-msg-row other">' + avatarHTML(id, chat.name.charAt(0)) + '<div class="qq-msg-content"><div class="qq-msg-bubble">' + m.text + '</div></div>' + '</div>'; } else { html = '<div class="qq-msg-row me">' + avatarHTML("zhuang", "庄") + '<div class="qq-msg-content"><div class="qq-msg-bubble">' + m.text + '</div></div>' + '</div>'; } $body.append(html); i++; setTimeout(addNext, 350); } addNext(); } $("#friend-list").on("click", ".qq-friend", function(){ if (yangSent) return; $(".qq-friend").removeClass("active"); $(this).addClass("active"); $("#chat-input").prop("disabled", true).val(""); $("#send-btn").prop("disabled", true).css("opacity", 0.4); loadChat($(this).data("friend")); }); $("#chat-input").on("input", function(){ if ($(this).val().trim()) { $("#send-btn").prop("disabled", false).css("opacity", 1); } else { $("#send-btn").prop("disabled", true).css("opacity", 0.4); } }); function sendMessage(){ var text = $("#chat-input").val().trim(); if (!text) return; if (currentFriend === "yang" && !yangSent) { yangSent = true; $("#chat-input").val("").prop("disabled", true); $("#send-btn").prop("disabled", true).css("opacity", 0.4); $(".qq-friend").off("click"); var row = '<div class="qq-msg-row me" style="position:relative;">' + avatarHTML("zhuang", "庄") + '<div class="qq-msg-content" style="position:relative;">' + '<div class="qq-msg-bubble" style="background:#8a3030;color:#f0d0d0;">' + text + '</div>' + '<span style="position:absolute;right:-22px;top:50%;transform:translateY(-50%);color:#f04a4a;font-size:18px;font-weight:bold;animation:failPop .3s;">!</span>' + '</div>' + '</div>'; var $body = $("#chat-body"); $body.append(row); $body.scrollTop($body[0].scrollHeight); setTimeout(function(){ SugarCube.Engine.play('序章-灰头像'); }, 1600); return; } $("#chat-input").val(""); $("#send-btn").prop("disabled", true).css("opacity", 0.4); var normalRow = '<div class="qq-msg-row me">' + avatarHTML("zhuang", "庄") + '<div class="qq-msg-content"><div class="qq-msg-bubble">' + text + '</div></div>' + '</div>'; var $b = $("#chat-body"); $b.append(normalRow); $b.scrollTop($b[0].scrollHeight); } $("#send-btn").on("click", sendMessage); $("#chat-input").on("keypress", function(e){ if (e.which === 13) sendMessage(); }); }); </script>
欧克了,你点了一下"恢复聊天记录"。 屏幕闪了一下。 然后,好友列表里,所有灰色的头像,一个一个,亮了起来。 一个,两个,三个……几十个。 只有她的头像,还是灰的。 然后—— <span class="avatar online" data-avatar="yang" style="display:inline-block;width:26px;height:26px;border-radius:50%;vertical-align:-6px;margin-right:6px;"></span> <span style="color:#d0d0dc">杨小语</span> 的头像,亮了一瞬。 <span style="color:#d0d0dc">又暗了下去。</span> 聊天框里,光标闪了两下。 <span style="color:#d0d0dc">你打了两个字。</span> <span style="color:#d0d0dc">但没有发送。</span> 屏幕的光,慢慢暗了下来。 <div style="text-align:center;color:#55555f;margin:40px 0;letter-spacing:.2em">——序章 完——</div> <div id="prologue-end-hint" style="text-align:center;color:#44444f;font-size:12px;letter-spacing:.3em;margin-top:60px;opacity:0;transition:opacity 1.5s ease;">点击任意处继续</div> <script> $(function(){ // 应用头像:查找本 passage 里所有 data-avatar 元素 $("[data-avatar]").each(function(){ var key = $(this).data("avatar"); if (window.AV && window.AV[key]) { this.style.setProperty("background-image", "url('" + window.AV[key] + "')", "important"); this.style.setProperty("background-size", "cover", "important"); this.style.setProperty("background-position", "center", "important"); this.style.setProperty("color", "transparent", "important"); } }); setTimeout(function(){ $("#prologue-end-hint").css("opacity", 1); }, 800); var clicked = false; function goNext(){ if (clicked) return; clicked = true; $(document).off("click.prologueKeydown"); $(document).off("keydown.prologueKeydown"); SugarCube.Engine.play("第一幕"); } setTimeout(function(){ $(document).on("click.prologueKeydown", goNext); $(document).on("keydown.prologueKeydown", goNext); }, 1500); }); </script>
<div class="qq-window"> <div class="qq-navbar"> <div class="qq-nav-avatar" data-avatar="zhuang"><span class="qq-nav-dot">99+</span></div> <div class="qq-nav-item">💬</div> <div class="qq-nav-item active">👤</div> <div class="qq-nav-item">⭐</div> <div class="qq-nav-item">📁</div> <div class="qq-nav-item">📷</div> <div class="qq-nav-item">🎵</div> </div> <div class="qq-sidebar"> <div class="qq-me"> <div class="qq-me-avatar" data-avatar="zhuang"></div> <div class="qq-me-info"> <div class="qq-me-name">庄大同</div> <div class="qq-me-sign">[离线] 想不起来自己是谁</div> </div> </div> <div class="qq-search-wrap"> <input type="text" class="qq-search" placeholder="搜索"> </div> <div class="qq-list" id="friend-list"> <div class="qq-friend" data-friend="mama"> <div class="qq-friend-avatar gray">妈</div> <div class="qq-friend-info"> <div class="qq-friend-name">妈妈</div> <div class="qq-friend-last">[离线]</div> </div> </div> <div class="qq-friend" data-friend="yang"> <div class="qq-friend-avatar gray" data-avatar="yang"></div> <div class="qq-friend-info"> <div class="qq-friend-name">杨小语</div> <div class="qq-friend-last">他们说你喜欢我</div> </div> </div> <div class="qq-friend" data-friend="liuyixuan"> <div class="qq-friend-avatar gray" data-avatar="liuyixuan"></div> <div class="qq-friend-info"> <div class="qq-friend-name">刘亦轩</div> <div class="qq-friend-last">[离线]</div> </div> </div> <div class="qq-friend" data-friend="banzhuren"> <div class="qq-friend-avatar gray" data-avatar="banzhuren"></div> <div class="qq-friend-info"> <div class="qq-friend-name">班主任</div> <div class="qq-friend-last">[离线]</div> </div> </div> <div class="qq-friend" data-friend="suncheng"> <div class="qq-friend-avatar gray" data-avatar="suncheng"></div> <div class="qq-friend-info"> <div class="qq-friend-name">孙程</div> <div class="qq-friend-last">[离线]</div> </div> </div> <div class="qq-friend" data-friend="zhoujielun"> <div class="qq-friend-avatar gray" data-avatar="zhoujielun"></div> <div class="qq-friend-info"> <div class="qq-friend-name">周杰伦</div> <div class="qq-friend-last">[离线]</div> </div> </div> <div class="qq-friend" data-friend="wangyabo"> <div class="qq-friend-avatar gray" data-avatar="wangyabo"></div> <div class="qq-friend-info"> <div class="qq-friend-name">王亚博</div> <div class="qq-friend-last">[离线]</div> </div> </div> </div> </div> <div class="qq-chat"> <div class="qq-searchbar"> <span class="qq-searchbar-icon">🔍</span> <input type="text" id="searchInput" class="qq-searchbar-input" placeholder="搜索" autocomplete="off" autofocus> </div> <div class="qq-searchresult" id="searchResult"> <div class="qq-searchresult-title">常用功能</div> <div class="qq-searchrow"> <div class="qq-searchrow-avatar">💬</div> <div class="qq-searchrow-info"> <div class="qq-searchrow-name">搜索聊天记录</div> </div> </div> <div class="qq-searchrow"> <div class="qq-searchrow-avatar">📁</div> <div class="qq-searchrow-info"> <div class="qq-searchrow-name">搜索文件</div> </div> </div> </div> </div> </div> <script> $(function(){ function applyAllAvatars(root){ var $scope = root ? $(root) : $(document); $scope.find("[data-avatar]").addBack("[data-avatar]").each(function(){ var key = $(this).data("avatar"); if (window.AV && window.AV[key]) { this.style.setProperty("background-image", "url('" + window.AV[key] + "')", "important"); this.style.setProperty("background-size", "cover", "important"); this.style.setProperty("background-position", "center", "important"); this.style.setProperty("background-repeat", "no-repeat", "important"); this.style.setProperty("color", "transparent", "important"); } }); } applyAllAvatars(); var contacts = [ { name: "杨小语", desc: "联系人", key: "yang", avatar: "yang" }, { name: "妈妈", desc: "联系人", key: "mama" }, { name: "刘亦轩", desc: "联系人", key: "liuyixuan", avatar: "liuyixuan" }, { name: "班主任", desc: "联系人", key: "banzhuren", avatar: "banzhuren" }, { name: "孙程", desc: "联系人", key: "suncheng", avatar: "suncheng" }, { name: "周杰伦", desc: "联系人", key: "zhoujielun", avatar: "zhoujielun" }, { name: "王亚博", desc: "联系人", key: "wangyabo", avatar: "wangyabo" }, { name: "爸爸", desc: "联系人", key: "baba" } ]; var $result = $("#searchResult"); var $input = $("#searchInput"); function renderSearch(keyword){ var html = ""; if (!keyword) { html += '<div class="qq-searchresult-title">常用功能</div>'; html += '<div class="qq-searchrow"><div class="qq-searchrow-avatar">💬</div><div class="qq-searchrow-info"><div class="qq-searchrow-name">搜索聊天记录</div></div></div>'; html += '<div class="qq-searchrow"><div class="qq-searchrow-avatar">📁</div><div class="qq-searchrow-info"><div class="qq-searchrow-name">搜索文件</div></div></div>'; } else { var matched = contacts.filter(function(c){ return c.name.indexOf(keyword) >= 0; }); if (matched.length > 0) { html += '<div class="qq-searchresult-title">联系人</div>'; matched.forEach(function(c){ var avatarAttr = c.avatar ? ' data-avatar="' + c.avatar + '"' : ''; var avatarContent = c.avatar ? '' : c.name.charAt(0); html += '<div class="qq-searchrow qq-search-contact" data-key="' + c.key + '" data-name="' + c.name + '">' + '<div class="qq-searchrow-avatar"' + avatarAttr + '>' + avatarContent + '</div>' + '<div class="qq-searchrow-info">' + '<div class="qq-searchrow-name">' + c.name + '</div>' + '<div class="qq-searchrow-desc">' + c.desc + '</div>' + '</div>' + '</div>'; }); } else { html += '<div class="qq-searchresult-empty">无搜索结果</div>'; } } $result.html(html); applyAllAvatars($result); } $input.on("input", function(){ renderSearch($(this).val().trim()); }); $result.on("click", ".qq-search-contact", function(){ var key = $(this).data("key"); if (key === "yang") { SugarCube.Engine.play("第一幕-碎片1"); } else { var $row = $(this); if ($row.data("clicked")) return; $row.data("clicked", true); $row.css("background", "#2a2a38"); setTimeout(function(){ $row.css("background", ""); $row.data("clicked", false); }, 400); } }); setTimeout(function(){ $input.focus(); }, 200); }); </script>
<div class="qq-window"> <div class="qq-navbar"> <div class="qq-nav-avatar" data-avatar="zhuang"><span class="qq-nav-dot">99+</span></div> <div class="qq-nav-item">💬</div> <div class="qq-nav-item active">👤</div> <div class="qq-nav-item">⭐</div> <div class="qq-nav-item">📁</div> <div class="qq-nav-item">📷</div> <div class="qq-nav-item">🎵</div> </div> <div class="qq-sidebar"> <div class="qq-me"> <div class="qq-me-avatar" data-avatar="zhuang"></div> <div class="qq-me-info"><div class="qq-me-name">庄大同</div><div class="qq-me-sign">[在线] 手机在线</div></div> </div> <div class="qq-search-wrap"><input type="text" class="qq-search" placeholder="搜索"></div> <div class="qq-list"> <div class="qq-friend active"> <div class="qq-friend-avatar" data-avatar="yang"></div> <div class="qq-friend-info"><div class="qq-friend-name">杨小语</div><div class="qq-friend-last">晚安</div></div> </div> <div class="qq-friend"> <div class="qq-friend-avatar gray" data-avatar="liuyixuan"></div> <div class="qq-friend-info"><div class="qq-friend-name">刘亦轩</div><div class="qq-friend-last">[离线]</div></div> </div> </div> </div> <div class="qq-chat"> <div class="qq-chat-header"> <div><span class="qq-chat-name">杨小语</span><span class="qq-chat-status">[在线]</span></div> <div class="qq-chat-tools"><span>📞</span><span>📹</span><span>📁</span><span>⋯</span></div> </div> <div class="qq-chat-body"> <div class="qq-divider"><span>2018-10-XX 22:40</span></div> <div class="qq-msg-row other"><div class="qq-msg-avatar" data-avatar="yang"></div><div class="qq-msg-content"><div class="qq-msg-bubble">在干嘛</div></div></div> <div class="qq-msg-row me"><div class="qq-msg-avatar" data-avatar="zhuang"></div><div class="qq-msg-content"><div class="qq-msg-bubble">没干嘛</div></div></div> <div class="qq-msg-row other"><div class="qq-msg-avatar" data-avatar="yang"></div><div class="qq-msg-content"><div class="qq-msg-bubble">哈哈哈 我也是</div></div></div> <div class="qq-divider"><span>2018-11-XX 23:15</span></div> <div class="qq-msg-row other"><div class="qq-msg-avatar" data-avatar="yang"></div><div class="qq-msg-content"><div class="qq-msg-bubble">你睡了吗</div></div></div> <div class="qq-msg-row me"><div class="qq-msg-avatar" data-avatar="zhuang"></div><div class="qq-msg-content"><div class="qq-msg-bubble">没有</div></div></div> <div class="qq-msg-row other"><div class="qq-msg-avatar" data-avatar="yang"></div><div class="qq-msg-content"><div class="qq-msg-bubble">那聊会儿</div></div></div> <div class="qq-msg-row me"><div class="qq-msg-avatar" data-avatar="zhuang"></div><div class="qq-msg-content"><div class="qq-msg-bubble">好</div></div></div> <div class="qq-divider"><span>2019-01-XX 00:02</span></div> <div class="qq-msg-row other"><div class="qq-msg-avatar" data-avatar="yang"></div><div class="qq-msg-content"><div class="qq-msg-bubble">晚安</div></div></div> <div class="qq-msg-row me"><div class="qq-msg-avatar" data-avatar="zhuang"></div><div class="qq-msg-content"><div class="qq-msg-bubble">晚安</div></div></div> </div> <div class="qq-chat-footer"> <div class="qq-toolbar"><span>😊</span><span>✂</span><span>📁</span><span>📷</span><span>🎤</span></div> <div class="qq-input-row"> <input type="text" class="qq-chat-input" placeholder="输入消息…"> <button class="qq-chat-send" disabled>发送</button> </div> </div> </div> </div> <div class="click-hint" id="hint">点击任意处继续</div> <script> $(function(){ setTimeout(function(){ $("#hint").addClass("show"); }, 600); var done = false; setTimeout(function(){ $(document).on("click.firstChat keydown.firstChat", function(){ if (done) return; done = true; $(document).off(".firstChat"); SugarCube.Engine.play("第一幕-结束"); }); }, 1200); }); </script>
<div class="qq-window"> <div class="qq-navbar"> <div class="qq-nav-avatar">庄<span class="qq-nav-dot">99+</span></div> <div class="qq-nav-item">💬</div> <div class="qq-nav-item active">👤</div> <div class="qq-nav-item">⭐</div> <div class="qq-nav-item">📁</div> <div class="qq-nav-item">📷</div> <div class="qq-nav-item">🎵</div> </div> <div class="qq-sidebar"> <div class="qq-me"> <div class="qq-me-avatar">庄</div> <div class="qq-me-info"><div class="qq-me-name">庄大同</div><div class="qq-me-sign">[在线] 手机在线</div></div> </div> <div class="qq-search-wrap"><input type="text" class="qq-search" placeholder="搜索"></div> <div class="qq-list"> <div class="qq-friend active"> <div class="qq-friend-avatar">杨</div> <div class="qq-friend-info"><div class="qq-friend-name">杨小语</div><div class="qq-friend-last">晚安</div></div> </div> <div class="qq-friend"> <div class="qq-friend-avatar gray">同</div> <div class="qq-friend-info"><div class="qq-friend-name">同桌</div><div class="qq-friend-last">[离线]</div></div> </div> </div> </div> <div class="qq-chat"> <div class="qq-chat-header"> <div><span class="qq-chat-name">杨小语</span><span class="qq-chat-status">[在线]</span></div> <div class="qq-chat-tools"><span>📞</span><span>📹</span><span>📁</span><span>⋯</span></div> </div> <div class="qq-chat-body"> <div class="qq-divider"><span>2018-10-XX 22:40</span></div> <div class="qq-msg-row other"><div class="qq-msg-avatar">杨</div><div class="qq-msg-content"><div class="qq-msg-bubble">在干嘛</div></div></div> <div class="qq-msg-row me"><div class="qq-msg-avatar">庄</div><div class="qq-msg-content"><div class="qq-msg-bubble">没干嘛</div></div></div> <div class="qq-msg-row other"><div class="qq-msg-avatar">杨</div><div class="qq-msg-content"><div class="qq-msg-bubble">哈哈哈 我也是</div></div></div> <div class="qq-divider"><span>2018-11-XX 23:15</span></div> <div class="qq-msg-row other"><div class="qq-msg-avatar">杨</div><div class="qq-msg-content"><div class="qq-msg-bubble">你睡了吗</div></div></div> <div class="qq-msg-row me"><div class="qq-msg-avatar">庄</div><div class="qq-msg-content"><div class="qq-msg-bubble">没有</div></div></div> <div class="qq-msg-row other"><div class="qq-msg-avatar">杨</div><div class="qq-msg-content"><div class="qq-msg-bubble">那聊会儿</div></div></div> <div class="qq-msg-row me"><div class="qq-msg-avatar">庄</div><div class="qq-msg-content"><div class="qq-msg-bubble">好</div></div></div> <div class="qq-divider"><span>2019-01-XX 00:02</span></div> <div class="qq-msg-row other"><div class="qq-msg-avatar">杨</div><div class="qq-msg-content"><div class="qq-msg-bubble">晚安</div></div></div> <div class="qq-msg-row me"><div class="qq-msg-avatar">庄</div><div class="qq-msg-content"><div class="qq-msg-bubble">晚安</div></div></div> </div> <div class="qq-chat-footer"> <div class="qq-toolbar"><span>😊</span><span>✂</span><span>📁</span><span>📷</span><span>🎤</span></div> <div class="qq-input-row"> <input type="text" class="qq-chat-input" placeholder="输入消息…"> <button class="qq-chat-send" disabled>发送</button> </div> </div> </div> </div> <div class="click-hint" id="hint">点击任意处继续</div> <script> $(function(){ setTimeout(function(){ $("#hint").addClass("show"); }, 600); var done = false; setTimeout(function(){ $(document).on("click.firstChat keydown.firstChat", function(){ if (done) return; done = true; $(document).off(".firstChat"); SugarCube.Engine.play("第一幕-结束"); }); }, 1200); }); </script>
聊天记录的最后一条,停在2019年的冬天。 之后,是一段空白。 <span style="color:#55555f">(没有记录。什么都没有。)</span> 你盯着那片空白。 <span style="color:#d0d0dc">你知道,再往下,就是疫情了。</span> <div style="text-align:center;color:#55555f;margin:40px 0;letter-spacing:.2em">——第一幕 完——</div> <div class="click-hint" id="hint">点击任意处继续</div> <script> $(function(){ setTimeout(function(){ $("#hint").addClass("show"); }, 600); var done = false; setTimeout(function(){ $(document).on("click.act1End keydown.act1End", function(){ if (done) return; done = true; $(document).off(".act1End"); SugarCube.Engine.play("第二幕"); }); }, 1200); }); </script>
你点开一个旧的网课平台图标。 <div class="screen"> 正在加载……<br> 2020年 春季学期<br> 班级群:六年二班 </div> 群成员列表很长。你往下滑。 大部分头像是灰的。 滑到最下面—— <span class="avatar online" data-avatar="yang" style="display:inline-block;width:26px;height:26px;border-radius:50%;vertical-align:-6px;margin-right:6px;"></span> <span style="color:#d0d0dc">杨小语</span> 她的头像,也是灰的。 旁边有一行小字: <span style="color:#55555f">最后在线:2020-03-XX</span> <div class="click-hint" id="hint">点击任意处继续</div> <script> $(function(){ setTimeout(function(){ $("#hint").addClass("show"); }, 600); var done = false; setTimeout(function(){ $(document).on("click.act2 keydown.act2", function(){ if (done) return; done = true; $(document).off(".act2"); SugarCube.Engine.play("第二幕-聊天"); }); }, 1200); }); </script>
私聊窗口打开了。 消息不多,但时间跨度很长。从2020年1月,一直到3月。 <span style="color:#55555f">(按时间顺序,点击下面的消息)</span> <div id="sort-area"> <div class="sort-item" data-order="2">2020-02-XX 杨小语:在干嘛</div> <div class="sort-item" data-order="4">2020-03-XX 杨小语:他们说你喜欢我</div> <div class="sort-item" data-order="1">2020-01-XX 杨小语:新年快乐</div> <div class="sort-item" data-order="3">2020-02-XX 庄大同:没干嘛</div> </div> <div id="sort-slots"> <div class="sort-slot" data-slot="1">1.</div> <div class="sort-slot" data-slot="2">2.</div> <div class="sort-slot" data-slot="3">3.</div> <div class="sort-slot" data-slot="4">4.</div> </div> <div id="sort-msg" style="margin-top:12px;min-height:1.4em"></div> <script> $(function(){ var placed = []; $(".sort-item").click(function(){ if ($(this).hasClass("placed")) return; var order = $(this).data("order"); var slot = $(".sort-slot[data-slot='" + (placed.length + 1) + "']"); if (slot.length) { slot.text((placed.length + 1) + ". " + $(this).text()); slot.addClass("placed"); $(this).addClass("placed").hide(); placed.push(order); if (placed.length === 4) { var ok = (placed[0]==1 && placed[1]==2 && placed[2]==3 && placed[3]==4); if (ok) { $("#sort-msg").html('<span style="color:#7fc8a8">顺序正确。</span>'); setTimeout(function(){ SugarCube.Engine.play("第二幕-断联"); }, 900); } else { $("#sort-msg").html('<span style="color:#d08a8a">顺序不对。刷新页面重试。</span>'); } } } }); }); </script>
消息按时间排好了。 <div class="qq-window"> <div class="qq-navbar"> <div class="qq-nav-avatar" data-avatar="zhuang"><span class="qq-nav-dot">99+</span></div> <div class="qq-nav-item">💬</div> <div class="qq-nav-item active">👤</div> <div class="qq-nav-item">⭐</div> <div class="qq-nav-item">📁</div> <div class="qq-nav-item">📷</div> <div class="qq-nav-item">🎵</div> </div> <div class="qq-sidebar"> <div class="qq-me"> <div class="qq-me-avatar" data-avatar="zhuang"></div> <div class="qq-me-info"> <div class="qq-me-name">庄大同</div> <div class="qq-me-sign">[在线]</div> </div> </div> <div class="qq-search-wrap"><input type="text" class="qq-search" placeholder="搜索"></div> <div class="qq-list"> <div class="qq-friend active"> <div class="qq-friend-avatar gray" data-avatar="yang"></div> <div class="qq-friend-info"> <div class="qq-friend-name">杨小语</div> <div class="qq-friend-last">他们说你喜欢我</div> </div> </div> <div class="qq-friend"> <div class="qq-friend-avatar gray">妈</div> <div class="qq-friend-info"><div class="qq-friend-name">妈妈</div><div class="qq-friend-last">[离线]</div></div> </div> <div class="qq-friend"> <div class="qq-friend-avatar gray" data-avatar="liuyixuan"></div> <div class="qq-friend-info"><div class="qq-friend-name">刘亦轩</div><div class="qq-friend-last">[离线]</div></div> </div> </div> </div> <div class="qq-chat"> <div class="qq-chat-header"> <div><span class="qq-chat-name">杨小语</span><span class="qq-chat-status">[离线]</span></div> <div class="qq-chat-tools"><span>📞</span><span>📹</span><span>📁</span><span>⋯</span></div> </div> <div class="qq-chat-body"> <div class="qq-divider"><span>2020-01-XX</span></div> <div class="qq-msg-row other"><div class="qq-msg-avatar" data-avatar="yang"></div><div class="qq-msg-content"><div class="qq-msg-bubble">新年快乐</div></div></div> <div class="qq-msg-row me"><div class="qq-msg-avatar" data-avatar="zhuang"></div><div class="qq-msg-content"><div class="qq-msg-bubble">新年快乐</div></div></div> <div class="qq-divider"><span>2020-02-XX</span></div> <div class="qq-msg-row other"><div class="qq-msg-avatar" data-avatar="yang"></div><div class="qq-msg-content"><div class="qq-msg-bubble">在干嘛</div></div></div> <div class="qq-msg-row me"><div class="qq-msg-avatar" data-avatar="zhuang"></div><div class="qq-msg-content"><div class="qq-msg-bubble">没干嘛</div></div></div> <div class="qq-divider"><span>2020-03-XX</span></div> <div class="qq-msg-row other"><div class="qq-msg-avatar" data-avatar="yang"></div><div class="qq-msg-content"><div class="qq-msg-bubble">他们说你喜欢我</div></div></div> </div> <div class="qq-chat-footer"> <div class="qq-toolbar"><span>😊</span><span>✂</span><span>📁</span><span>📷</span><span>🎤</span></div> <div class="qq-input-row"> <div class="qq-chat-input">输入消息……</div> <button class="qq-chat-send" disabled>发送</button> </div> </div> </div> </div> 然后,就没有然后了。 你点了一下发送框,想打点什么。光标闪了一下。<span style="color:#55555f">你打不出字。</span> 你点她的头像。弹出一行提示: <div class="screen" style="color:#d08a8a"> 对方已开启好友验证。<br> 你还不是他(她)好友。 </div> 你返回好友列表。一个头像变灰。两个。三个。 几十个头像,一个一个,暗下去。 像有人在一间一间,关灯。 <div class="fragment">记忆碎片 +1 (2/7)</div> <<set $memories += 1>> [[试着恢复被删的消息->第二幕-恢复]]
你点了一下“恢复聊天记录”。 屏幕闪了一下。 聊天框里,慢慢浮出一行灰色的字。 <div class="screen" style="color:#55555f"> [已删除] 庄大同:不是 </div> 你不记得自己打过这两个字。 但你知道,你打了。而且,没有发出去。 <span style="color:#d0d0dc">红色感叹号,一直挂在那里。</span> <div class="fragment">记忆碎片 +1 (3/7)</div> <<set $memories += 1>> <div style="text-align:center;color:#55555f;margin:40px 0;letter-spacing:.2em">——第二幕 完——</div> <div class="click-hint" id="hint">点击任意处继续</div> <script> $(function(){ setTimeout(function(){ $("#hint").addClass("show"); }, 600); var done = false; setTimeout(function(){ $(document).on("click.act2End keydown.act2End", function(){ if (done) return; done = true; $(document).off(".act2End"); SugarCube.Engine.play("第三幕"); }); }, 1200); }); </script>
2021年,初二。 你推开教室的门。 黑板上写着: <div class="note">按身高排座 · 本周调整</div> <span style="color:#55555f">(点击你右边的空座位)</span> <div class="seat-grid"> <div class="seat named">孙程</div><div class="seat named">周杰伦</div><div class="seat named">王亚博</div><div class="seat named">赵磊</div><div class="seat named">孙婷</div><div class="seat named">周浩</div> <div class="seat named">吴敏</div><div class="seat named">郑凯</div><div class="seat me">庄大同</div><div class="seat empty" data-pos="4">?</div><div class="seat named">林琳</div><div class="seat named">黄强</div> <div class="seat named">徐静</div><div class="seat named">马超</div><div class="seat named">朱莉</div><div class="seat named">何伟</div><div class="seat named">高圆</div><div class="seat named">罗杰</div> </div> <div id="seat-msg" style="margin-top:12px;min-height:1.4em"></div> <div id="seat-next" style="margin-top:20px"></div> <script> $(function(){ $(".seat.empty").click(function(){ if ($(this).data("pos") === 4) { $(this).removeClass("empty").addClass("right").text("杨小语"); $("#seat-msg").html('<span style="color:#7fc8a8">你想起来了。她就坐在你旁边。</span>'); $("#seat-next").html('<a class="link-internal" href="javascript:void(0)" onclick="SugarCube.Engine.play(\'第三幕-重逢\')">继续 →</a>'); } else { $(this).addClass("wrong").text("空"); $("#seat-msg").html('<span style="color:#d08a8a">这里没有人。</span>'); var self = this; setTimeout(function(){ $(self).removeClass("wrong").text("?"); }, 800); } }); }); </script>
她坐了下来。 书包放在桌角,橡皮推过来。 <span style="color:#d0d0dc">“你用。”</span> 你说:“不用。” 她没再说话,把橡皮收了回去。 但你不记得她的脸。你只是,看着这个画面,觉得熟悉。 <div class="screen"> 2021-09-XX<br> 杨小语:好巧啊 又同桌<br> 庄大同:嗯<br> 杨小语:哈哈哈 你还是这样 </div> 她还是发“哈哈哈”。你还是回“嗯”。 <div class="fragment">记忆碎片 +1 (4/7)</div> <<set $memories += 1>> <div class="click-hint" id="hint">点击任意处继续</div> <script> $(function(){ setTimeout(function(){ $("#hint").addClass("show"); }, 600); var done = false; setTimeout(function(){ $(document).on("click.act3Again keydown.act3Again", function(){ if (done) return; done = true; $(document).off(".act3Again"); SugarCube.Engine.play("第三幕-自卑"); }); }, 1200); }); </script>
那一年,你们又变得亲密无间。 一起值日。一起抄作业。一起在走廊上,被老师叫住,然后一起低头挨训。 但有一件事,你一直很清楚。 <div class="note"> 她坐在你旁边。<br> 她比你高半个头。<br> 她笑起来的时候,整个教室都亮。<br> 而你,又矮,又挫,成绩一般,话也少。 </div> 你看着镜子里的自己。 <span style="color:#d0d0dc">你知道,你配不上她。</span> 所以你对自己说: <span style="color:#d0d0dc">“就当普通朋友吧。”</span> <span style="color:#d0d0dc">“这样就好。”</span> 你把这句话,在心里说了一千遍。 说到自己都信了。 <div class="click-hint" id="hint">点击任意处继续</div> <script> $(function(){ setTimeout(function(){ $("#hint").addClass("show"); }, 600); var done = false; setTimeout(function(){ $(document).on("click.act3Self keydown.act3Self", function(){ if (done) return; done = true; $(document).off(".act3Self"); SugarCube.Engine.play("第三幕-裂痕"); }); }, 1200); }); </script>
后来,事情慢慢变了。 你不记得是哪一天开始的。 也许是你某句话说错了。也许是你某次没有回她消息。 你只记得,她开始不回你消息了。 <div class="screen"> 庄大同:在吗<br> 庄大同:?<br> 庄大同:对不起<br> <span style="color:#55555f">(没有回复)</span> </div> 你打了很多字。删了。又打。又删。 最后,你什么都没发。 <span style="color:#55555f">(有些话,过了那个时间,就再也说不出口了。)</span> <div style="text-align:center;color:#55555f;margin:40px 0;letter-spacing:.2em">——第三幕 完——</div> <div class="click-hint" id="hint">点击任意处继续</div> <script> $(function(){ setTimeout(function(){ $("#hint").addClass("show"); }, 600); var done = false; setTimeout(function(){ $(document).on("click.act3End keydown.act3End", function(){ if (done) return; done = true; $(document).off(".act3End"); SugarCube.Engine.play("第四幕"); }); }, 1200); }); </script>
2022年,初三。 你翻着聊天记录,想找出是哪一天开始,她不再回你消息。 但记录是碎的。有一些,你删了。有一些,她不在了。有一些,你根本不敢看。 <span style="color:#55555f">(点开每一句,看看你记得哪些)</span> <div class="choice-item" data-id="1">“你别老是这样。”</div> <div class="choice-item" data-id="2">“我配不上你。”</div> <div class="choice-item" data-id="3">“随便你吧。”</div> <div class="choice-item" data-id="4">“我们以后还是少联系吧。”</div> <div id="choice-msg" style="margin-top:12px;min-height:1.4em"></div> <script> $(function(){ var seen = []; $(".choice-item").click(function(){ if ($(this).hasClass("checked")) return; $(this).addClass("checked"); seen.push($(this).data("id")); $("#choice-msg").html('<span style="color:#55555f">你记得你说过这句。但你不确定是不是这句。</span>'); if (seen.length === 4) { setTimeout(function(){ SugarCube.Engine.play("第四幕-真相"); }, 1200); } }); }); </script>
你看完了四条。 每一条,你都记得。每一条,你都忘了。 <span style="color:#d0d0dc">你不记得,是哪一句让她彻底讨厌你。</span> <span style="color:#d0d0dc">也许是全部。也许一句都不是。</span> 你只记得,从某一天开始——她不再发"哈哈哈",不再问"在干嘛",不再说"晚安"。 <div class="qq-window"> <div class="qq-navbar"> <div class="qq-nav-avatar" data-avatar="zhuang"><span class="qq-nav-dot">99+</span></div> <div class="qq-nav-item">💬</div> <div class="qq-nav-item active">👤</div> <div class="qq-nav-item">⭐</div> <div class="qq-nav-item">📁</div> <div class="qq-nav-item">📷</div> <div class="qq-nav-item">🎵</div> </div> <div class="qq-sidebar"> <div class="qq-me"> <div class="qq-me-avatar" data-avatar="zhuang"></div> <div class="qq-me-info"><div class="qq-me-name">庄大同</div><div class="qq-me-sign">[在线]</div></div> </div> <div class="qq-search-wrap"><input type="text" class="qq-search" placeholder="搜索"></div> <div class="qq-list"> <div class="qq-friend active"> <div class="qq-friend-avatar gray" data-avatar="yang"></div> <div class="qq-friend-info"><div class="qq-friend-name">杨小语</div><div class="qq-friend-last">[离线]</div></div> </div> </div> </div> <div class="qq-chat"> <div class="qq-chat-header"> <div><span class="qq-chat-name">杨小语</span><span class="qq-chat-status">[离线]</span></div> <div class="qq-chat-tools"><span>📞</span><span>📹</span><span>📁</span><span>⋯</span></div> </div> <div class="qq-chat-body"> <div class="qq-divider"><span>2022-XX-XX</span></div> <div class="qq-msg-row other" style="opacity:.5"><div class="qq-msg-avatar" data-avatar="yang"></div><div class="qq-msg-content"><div class="qq-msg-bubble">我们以后还是少联系吧。</div></div></div> <div class="qq-empty">(没有回复)</div> </div> </div> </div> <div class="fragment">记忆碎片 +1 (5/7)</div> <<set $memories += 1>> <div class="click-hint" id="hint">点击任意处继续</div> <script> $(function(){ setTimeout(function(){ $("#hint").addClass("show"); }, 600); var done = false; setTimeout(function(){ $(document).on("click.act4 keydown.act4", function(){ if (done) return; done = true; $(document).off(".act4"); SugarCube.Engine.play("第四幕-没有道歉"); }); }, 1200); }); </script>
你没有道歉。 不是不想。是不知道该怎么开口。 你打了很多次字。 <div class="screen"> 庄大同:对不起<br> 庄大同:我错了<br> 庄大同:你别这样<br> 庄大同:在吗 </div> 每一句,都停在发送框里。 红色感叹号,一个一个亮起来。 <span style="color:#55555f">(有些话,来不及说,就永远说不出了。)</span> <div style="text-align:center;color:#55555f;margin:40px 0;letter-spacing:.2em">——第四幕 完——</div> <div class="click-hint" id="hint">点击任意处继续</div> <script> $(function(){ setTimeout(function(){ $("#hint").addClass("show"); }, 600); var done = false; setTimeout(function(){ $(document).on("click.act4End keydown.act4End", function(){ if (done) return; done = true; $(document).off(".act4End"); SugarCube.Engine.play("第五幕"); }); }, 1200); }); </script>
2023年,夏天。中考结束。 班主任发给每个人一本毕业相册。你翻开它。 <span style="color:#55555f">(在照片里,找到她。)</span> <div class="photo-wrap" id="photo-wrap"> <img src="https://s41.ax1x.com/2026/10/01/pnGIQpR.jpg" alt="毕业照" class="photo-img" draggable="false"> <div id="photo-hotspots"></div> </div> <div id="photo-msg" style="margin-top:14px;min-height:1.4em;color:#55555f"></div> <!-- 弹窗:默认隐藏 --> <div id="found-modal" class="found-modal" aria-hidden="true"> <div class="found-modal-box"> <div class="found-modal-text">你找到了她。</div> <div class="found-modal-sub">她的脸被阳光照得发白。</div> <button id="found-modal-btn" class="found-modal-btn">继续 →</button> </div> </div> <style> .photo-wrap { position: relative; display: inline-block; max-width: 100%; margin-top: 14px; user-select: none; -webkit-tap-highlight-color: transparent; } .photo-img { display: block; max-width: 100%; height: auto; border-radius: 4px; filter: sepia(.12) contrast(.96) brightness(1.02); } .photo-hotspot { position: absolute; cursor: pointer; background: transparent; border: none; box-shadow: none; outline: none; } .photo-hotspot.found { cursor: default; } /* 弹窗遮罩 */ .found-modal { position: fixed; inset: 0; background: rgba(0, 0, 0, 0.72); display: none; align-items: center; justify-content: center; z-index: 9999; padding: 24px; backdrop-filter: blur(3px); -webkit-backdrop-filter: blur(3px); } .found-modal.show { display: flex; animation: foundFade 0.5s ease both; } @keyframes foundFade { from { opacity: 0; } to { opacity: 1; } } /* 弹窗卡片 */ .found-modal-box { background: #14141b; border: 1px solid #2a2a38; border-radius: 8px; padding: 42px 48px 36px; max-width: 420px; width: 100%; text-align: center; font-family: "Songti SC", "STSong", "SimSun", "Noto Serif SC", serif; box-shadow: 0 24px 80px rgba(0, 0, 0, 0.6), 0 0 40px rgba(180, 180, 220, 0.06); animation: foundRise 0.6s cubic-bezier(.2,.8,.2,1) both; animation-delay: 0.15s; } @keyframes foundRise { from { opacity: 0; transform: translateY(16px) scale(0.97); } to { opacity: 1; transform: translateY(0) scale(1); } } .found-modal-text { color: #e8e8f2; font-size: 18px; letter-spacing: .22em; text-indent: .22em; margin-bottom: 16px; } .found-modal-sub { color: #8a8a98; font-size: 14px; letter-spacing: .12em; line-height: 1.9; margin-bottom: 34px; } .found-modal-btn { padding: 10px 32px; background: transparent; border: 1px solid #3a3a48; color: #a8a8b8; font-family: inherit; font-size: 13px; letter-spacing: .35em; text-indent: .35em; cursor: pointer; border-radius: 2px; transition: color .35s ease, border-color .35s ease, box-shadow .35s ease; } .found-modal-btn:hover { color: #e8e8f2; border-color: #6a6a78; box-shadow: 0 0 20px rgba(180, 180, 220, 0.18); } </style> <script> $(function(){ var YANG = { left: 46, top: 44, width: 9, height: 10 }; var studentRows = [ { top: 14, h: 10 }, { top: 25, h: 10 }, { top: 36, h: 10 }, { top: 47, h: 10 }, { top: 57, h: 10 } ]; var studentCols = [5, 16.5, 28, 39.5, 51, 62.5, 74, 85.5]; var teacherY = 67, teacherH = 12; var teacherCols = [2, 9.5, 17, 24.5, 32, 39.5, 47, 54.5, 62, 69.5, 77, 84.5]; var $wrap = $("#photo-hotspots"); function overlap(a, b){ return !(a.x+a.w<=b.x || a.x>=b.x+b.w || a.y+a.h<=b.y || a.y>=b.y+b.h); } var yangRect = { x: YANG.left, y: YANG.top, w: YANG.width, h: YANG.height }; studentRows.forEach(function(row){ studentCols.forEach(function(left){ var c = { x: left, y: row.top, w: 11, h: row.h }; if (overlap(c, yangRect)) return; var $h = $('<div class="photo-hotspot"></div>').css({ left: left + "%", top: row.top + "%", width: "11%", height: row.h + "%" }).data("yang", false); $wrap.append($h); }); }); teacherCols.forEach(function(left){ var c = { x: left, y: teacherY, w: 7, h: teacherH }; if (overlap(c, yangRect)) return; var $h = $('<div class="photo-hotspot"></div>').css({ left: left + "%", top: teacherY + "%", width: "7%", height: teacherH + "%" }).data("yang", false); $wrap.append($h); }); var $yang = $('<div class="photo-hotspot"></div>').css({ left: YANG.left + "%", top: YANG.top + "%", width: YANG.width + "%", height: YANG.height + "%" }).data("yang", true); $wrap.append($yang); var found = false; var wrong = ["不是她。","也不是。","记忆里的脸,有点模糊。","这个不是。","再找找。"]; $wrap.on("click", ".photo-hotspot", function(){ if (found) return; if ($(this).data("yang")) { found = true; // 弹窗出现 $("#found-modal").addClass("show").attr("aria-hidden", "false"); } else { $("#photo-msg").html('<span style="color:#55555f">' + wrong[Math.floor(Math.random()*wrong.length)] + '</span>'); } }); // 弹窗按钮 → 跳到下一章 $("#found-modal-btn").on("click", function(){ $("#found-modal").removeClass("show"); // 页面淡出后跳转 setTimeout(function(){ SugarCube.Engine.play("第五幕-同学录"); }, 1000); }); // Shift + 点击调试坐标 $("#photo-wrap").on("click", function(e){ if (!e.shiftKey) return; e.preventDefault(); var rect = this.getBoundingClientRect(); var x = ((e.clientX - rect.left) / rect.width * 100).toFixed(1); var y = ((e.clientY - rect.top ) / rect.height * 100).toFixed(1); console.log("var YANG = { left: " + (x - 4.5).toFixed(1) + ", top: " + (y - 5).toFixed(1) + ", width: 9, height: 10 };"); }); }); </script>
你翻到相册的最后一页。 背面,是一张同学录。 她的字很小,但很清楚。 <div class="note" style="line-height:2.2"> 杨小语<br><br> “祝你以后越来越好。<br> 要长高啊。<br> —— 杨小语” </div> 你盯着最后那三个字看了很久。 <span style="color:#d0d0dc">“要长高啊。”</span> 这是她对你说的最后一句话。 不是讨厌。不是责怪。不是告别。 只是一句,很普通的,祝福。 <div class="fragment">记忆碎片 +1 (6/7)</div> <<set $memories += 1>> <div class="click-hint" id="hint">点击任意处继续</div> <script> $(function(){ setTimeout(function(){ $("#hint").addClass("show"); }, 600); var done = false; setTimeout(function(){ $(document).on("click.act5Record keydown.act5Record", function(){ if (done) return; done = true; $(document).off(".act5Record"); SugarCube.Engine.play("第五幕-失联"); }); }, 1200); }); </script>
中考结束之后,你再也没有见过她。 没有QQ消息。没有电话。没有偶遇。 她像是从这个世界上,消失了。 你有时候会想,她去了哪所高中。她过得怎么样。她有没有再遇到一个,坐在她左边,总是回“嗯”的同桌。 你不知道。 你也不会知道了。 <span style="color:#55555f">(记忆只剩最后一块了。)</span> <div style="text-align:center;color:#55555f;margin:40px 0;letter-spacing:.2em">——第五幕 完——</div> <div class="click-hint" id="hint">点击任意处继续</div> <script> $(function(){ setTimeout(function(){ $("#hint").addClass("show"); }, 600); var done = false; setTimeout(function(){ $(document).on("click.act5End keydown.act5End", function(){ if (done) return; done = true; $(document).off(".act5End"); SugarCube.Engine.play("终章"); }); }, 1200); }); </script>
<div id="letter-overlay" class="letter-overlay"> <div class="letter-paper"> <div class="letter-title">备忘录 · 未发送</div> <div class="letter-body"> <div class="letter-line">杨小语:</div> <div class="letter-space"></div> <div class="letter-line">我不记得自己是谁了。</div> <div class="letter-line">但我记得你。</div> <div class="letter-space"></div> <div class="letter-line">我记得你总发"哈哈哈"。</div> <div class="letter-line">我记得你说"要长高啊"。</div> <div class="letter-line">我记得你推过来的那块白橡皮。</div> <div class="letter-space"></div> <div class="letter-line">我忘了是哪一句让你讨厌我。</div> <div class="letter-line">也许是全部。也许一句都不是。</div> <div class="letter-space"></div> <div class="letter-line">但我想说——谢谢你。</div> <div class="letter-line">谢谢你,出现在我的生命里。</div> </div> <div class="letter-actions"> <button id="letter-send" class="letter-btn letter-btn-send">发送</button> <button id="letter-nosend" class="letter-btn letter-btn-nosend">不发送</button> </div> </div> </div> <style> .letter-overlay { position: fixed; inset: 0; background: rgba(0, 0, 0, 0.78); display: flex; align-items: center; justify-content: center; padding: 40px 20px; z-index: 100; backdrop-filter: blur(4px); -webkit-backdrop-filter: blur(4px); animation: letterFade 1.2s ease both; font-family: "Songti SC", "STSong", "SimSun", "Noto Serif SC", serif; } @keyframes letterFade { from { opacity: 0; } to { opacity: 1; } } .letter-paper { position: relative; width: 100%; max-width: 520px; max-height: 82vh; overflow-y: auto; padding: 42px 46px 34px; background: #10101a; border: 1px solid #26263a; border-radius: 6px; box-shadow: 0 30px 90px rgba(0, 0, 0, 0.7), 0 0 60px rgba(150, 150, 200, 0.05); animation: letterRise 1.4s cubic-bezier(.2,.8,.2,1) both; animation-delay: 0.3s; background-image: repeating-linear-gradient( to bottom, transparent 0, transparent 29px, rgba(180, 180, 220, 0.045) 29px, rgba(180, 180, 220, 0.045) 30px ); background-position: 0 84px; background-repeat: repeat; } @keyframes letterRise { from { opacity: 0; transform: translateY(24px); } to { opacity: 1; transform: translateY(0); } } .letter-paper::before { content: ""; position: absolute; top: 42px; bottom: 34px; left: 28px; width: 1px; background: linear-gradient( to bottom, transparent, rgba(180, 180, 220, 0.18) 15%, rgba(180, 180, 220, 0.18) 85%, transparent ); } .letter-title { font-size: 12px; letter-spacing: .48em; text-indent: .48em; color: #6a6a80; text-align: center; margin-bottom: 34px; padding-bottom: 18px; border-bottom: 1px solid rgba(180, 180, 220, 0.08); } .letter-body { font-size: 15px; line-height: 30px; color: #c4c4d2; letter-spacing: .06em; padding-left: 14px; } .letter-line { min-height: 30px; } .letter-space { height: 30px; } .letter-actions { margin-top: 36px; padding-top: 22px; border-top: 1px solid rgba(180, 180, 220, 0.08); display: flex; justify-content: flex-end; gap: 12px; } .letter-btn { padding: 8px 26px; background: transparent; font-family: inherit; font-size: 13px; letter-spacing: .32em; text-indent: .32em; border-radius: 2px; cursor: pointer; transition: color .35s ease, border-color .35s ease, background .35s ease, box-shadow .35s ease; } /* 发送:默认和"不发送"对称的淡边,点了会变 */ .letter-btn-send { border: 1px solid #2e2e3e; color: #8a8a98; } .letter-btn-send:hover { color: #e8e8f2; border-color: #6a6a78; box-shadow: 0 0 20px rgba(180, 180, 220, 0.12); } /* 点了发送后:变红、变灰、禁用 */ .letter-btn-send.failed { border-color: #6a3a3a; color: #8a5a5a; cursor: default; box-shadow: 0 0 18px rgba(160, 80, 80, 0.15); animation: failedShake .4s ease; } @keyframes failedShake { 0%, 100% { transform: translateX(0); } 20% { transform: translateX(-4px); } 40% { transform: translateX(4px); } 60% { transform: translateX(-3px); } 80% { transform: translateX(2px); } } .letter-btn-nosend { border: 1px solid #6a3a3a; color: #b58a8a; } .letter-btn-nosend:hover { color: #e0b0b0; border-color: #a05050; background: rgba(160, 80, 80, 0.06); box-shadow: 0 0 20px rgba(160, 80, 80, 0.14); } </style> <script> $(function(){ // ===================================================== // 两个按钮各自的跳转目标,改成你实际 passage 名字 // ===================================================== var PASSAGE_A = "终章-结局A"; // 不发送 var PASSAGE_B = "终章-结局B"; // 发送失败 // 只让这封信淡出,不动 body function fadeLetterOutAndGo(passageName){ var $overlay = $(".letter-overlay"); $overlay.css({ transition: "opacity .9s ease, transform .9s ease", opacity: 0, transform: "translateY(10px) scale(.98)" }); setTimeout(function(){ SugarCube.Engine.play(passageName); }, 950); } // ---- 发送按钮 ---- var $send = $("#letter-send"); $send.on("click", function(){ if ($send.hasClass("failed")) return; // 已经失败过就不再触发 // 按钮变红 → 显示"发送失败" $send.text("发送失败").addClass("failed"); // 1.2 秒后淡出这封信并跳转 setTimeout(function(){ fadeLetterOutAndGo(PASSAGE_B); }, 1200); }); // ---- 不发送按钮 ---- $("#letter-nosend").on("click", function(){ fadeLetterOutAndGo(PASSAGE_A); }); }); </script>
你把光标移到“发送”上。 手指停了一下。你点了下去。 <div class="screen"> 正在发送……<br> …… </div> 几秒钟后,一封退回的邮件,弹了出来。 <div class="screen" style="color:#d08a8a"> 发送失败。<br> 该地址已停用。 </div> 你看着那行字,很久。 然后你注意到——退回的邮件里,夹着一句话。 <div class="screen" style="color:#7fc8a8"> 杨小语:你要好好的。 </div> 那是很久以前,她发给你的。 你忘了。但你一直留着。 <div class="click-hint" id="hint">点击任意处继续</div> <script> $(function(){ setTimeout(function(){ $("#hint").addClass("show"); }, 600); var done = false; setTimeout(function(){ $(document).on("click.endSend keydown.endSend", function(){ if (done) return; done = true; $(document).off(".endSend"); SugarCube.Engine.play("终章-结局B"); }); }, 1200); }); </script>
你把光标移到"不发送"上。 手指停了一下。你没有点下去。 你只是,把草稿保存了。 <div class="screen"> 已保存到备忘录。<br> 未发送。 </div> 屏幕慢慢暗了下去。 你看着那个灰色的头像。 然后—— <span class="avatar online" data-avatar="yang" style="display:inline-block;width:26px;height:26px;border-radius:50%;vertical-align:-6px;margin-right:6px;"></span> <span style="color:#d0d0dc">杨小语</span> 的头像,亮了一瞬。 聊天框里,光标闪了两下。 像有人正在打字。 <span style="color:#d0d0dc">"对方正在输入……"</span> 然后,消失了。 <div class="click-hint" id="hint">点击任意处继续</div> <script> $(function(){ setTimeout(function(){ $("#hint").addClass("show"); }, 600); var done = false; setTimeout(function(){ $(document).on("click.endNoSend keydown.endNoSend", function(){ if (done) return; done = true; $(document).off(".endNoSend"); SugarCube.Engine.play("终章-结局A"); }); }, 1200); }); </script>
<div class="ending-screen"> <div class="ending-title">结局 B · 发送失败</div> <div class="ending-line" style="animation-delay: 1.2s"> "我也要好好的。" </div> <div class="ending-gap"></div> <div class="ending-line soft" style="animation-delay: 3.2s"> 你合上了电脑。 </div> <div class="ending-line soft" style="animation-delay: 4.4s"> 窗外天亮了。 </div> <div class="ending-line soft" style="animation-delay: 5.6s"> 你不知道她现在在哪,过得怎么样。 </div> <div class="ending-gap"></div> <div class="ending-line soft" style="animation-delay: 7.4s"> 但你知道一件事—— </div> <div class="ending-line strong" style="animation-delay: 8.6s"> 她希望你,好好的。 </div> <div class="ending-gap"></div> <div class="ending-line whisper" style="animation-delay: 10.8s"> (有些话,收不到,也算收到了。) </div> <button id="ending-btn" class="ending-btn" disabled> <span>继续</span> </button> </div> <style> .ending-screen { min-height: 70vh; display: flex; flex-direction: column; align-items: center; justify-content: center; padding: 60px 24px; text-align: center; font-family: "Songti SC", "STSong", "SimSun", "Noto Serif SC", serif; color: #c8c8d4; animation: endingFadeIn 2.2s ease both; } @keyframes endingFadeIn { from { opacity: 0; } to { opacity: 1; } } .ending-title { font-size: 13px; letter-spacing: .55em; color: #6a6a78; text-indent: .55em; margin-bottom: 42px; animation: endingRise 2s ease both; animation-delay: 0s; } .ending-line { opacity: 0; animation: endingRise 1.6s ease both; font-size: 15px; line-height: 2; letter-spacing: .12em; max-width: 520px; } .ending-line.soft { color: #8a8a98; } .ending-line.strong { color: #e8e8f2; font-size: 18px; letter-spacing: .2em; margin-top: 6px; } .ending-line.whisper { color: #55555f; font-size: 13px; font-style: italic; } .ending-gap { height: 22px; } @keyframes endingRise { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } } .ending-btn { margin-top: 56px; padding: 12px 38px; background: transparent; border: 1px solid #3a3a48; color: #8a8a98; font-family: inherit; font-size: 13px; letter-spacing: .4em; text-indent: .4em; cursor: pointer; border-radius: 2px; opacity: 0; transition: opacity 1.2s ease, color .5s ease, border-color .5s ease, box-shadow .5s ease; animation: btnAppear 2s ease forwards; animation-delay: 12.5s; animation-fill-mode: both; } @keyframes btnAppear { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: translateY(0); } } .ending-btn:hover:not(:disabled) { color: #e8e8f2; border-color: #6a6a78; box-shadow: 0 0 24px rgba(180, 180, 220, 0.12); } .ending-btn:disabled { cursor: default; } .ending-btn.ready { cursor: pointer; } </style> <script> $(function(){ var $btn = $("#ending-btn"); setTimeout(function(){ $btn.prop("disabled", false).addClass("ready"); }, 12800); var done = false; $btn.on("click", function(){ if ($btn.prop("disabled") || done) return; done = true; $(".ending-screen").css({ transition: "opacity .9s ease, transform .9s ease", opacity: 0, transform: "translateY(10px)" }); setTimeout(function(){ SugarCube.Engine.play("结局-作者的话"); }, 950); }); }); </script>
<div class="ending-screen"> <div class="ending-title">结局 A · 不发送</div> <div class="ending-line" style="animation-delay: 1.2s"> "谢谢你出现过。" </div> <div class="ending-gap"></div> <div class="ending-line soft" style="animation-delay: 3.2s"> 你合上了电脑。 </div> <div class="ending-line soft" style="animation-delay: 4.4s"> 窗外天亮了。 </div> <div class="ending-line soft" style="animation-delay: 5.6s"> 你不知道自己是不是真的想起了她。 </div> <div class="ending-gap"></div> <div class="ending-line soft" style="animation-delay: 7.4s"> 但你知道一件事—— </div> <div class="ending-line strong" style="animation-delay: 8.6s"> 你没有忘。 </div> <div class="ending-gap"></div> <div class="ending-line whisper" style="animation-delay: 10.8s"> (有些记忆,不需要发送。) </div> <button id="ending-btn" class="ending-btn" disabled> <span>继续</span> </button> </div> <style> .ending-screen { min-height: 70vh; display: flex; flex-direction: column; align-items: center; justify-content: center; padding: 60px 24px; text-align: center; font-family: "Songti SC", "STSong", "SimSun", "Noto Serif SC", serif; color: #c8c8d4; animation: endingFadeIn 2.2s ease both; } @keyframes endingFadeIn { from { opacity: 0; } to { opacity: 1; } } .ending-title { font-size: 13px; letter-spacing: .55em; color: #6a6a78; text-indent: .55em; margin-bottom: 42px; animation: endingRise 2s ease both; animation-delay: 0s; } .ending-line { opacity: 0; animation: endingRise 1.6s ease both; font-size: 15px; line-height: 2; letter-spacing: .12em; max-width: 520px; } .ending-line.soft { color: #8a8a98; } .ending-line.strong { color: #e8e8f2; font-size: 18px; letter-spacing: .2em; margin-top: 6px; } .ending-line.whisper { color: #55555f; font-size: 13px; font-style: italic; } .ending-gap { height: 22px; } @keyframes endingRise { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } } .ending-btn { margin-top: 56px; padding: 12px 38px; background: transparent; border: 1px solid #3a3a48; color: #8a8a98; font-family: inherit; font-size: 13px; letter-spacing: .4em; text-indent: .4em; cursor: pointer; border-radius: 2px; opacity: 0; transition: opacity 1.2s ease, color .5s ease, border-color .5s ease, box-shadow .5s ease; animation: btnAppear 2s ease forwards; animation-delay: 12.5s; animation-fill-mode: both; } @keyframes btnAppear { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: translateY(0); } } .ending-btn:hover:not(:disabled) { color: #e8e8f2; border-color: #6a6a78; box-shadow: 0 0 24px rgba(180, 180, 220, 0.12); } .ending-btn:disabled { cursor: default; } .ending-btn.ready { cursor: pointer; } </style> <script> $(function(){ var $btn = $("#ending-btn"); setTimeout(function(){ $btn.prop("disabled", false).addClass("ready"); }, 12800); var done = false; $btn.on("click", function(){ if ($btn.prop("disabled") || done) return; done = true; // 只淡出结局页本身,不动 body $(".ending-screen").css({ transition: "opacity .9s ease, transform .9s ease", opacity: 0, transform: "translateY(10px)" }); setTimeout(function(){ SugarCube.Engine.play("结局-作者的话"); }, 950); }); }); </script>
{ "ifid": "A1B2C3D4-E5F6-7890-ABCD-EF1234567890", "format": "SugarCube", "format-version": "2.37.3", "start": "序章" }
<div class="afterword-screen"> <div class="afterword-title">作者的话</div> <div class="afterword-body"> <p>这个故事,是从一个模糊的念头开始的——</p> <p class="soft">如果有一天,你想起了一个人,<br> 却已经想不起她的声音,<br> 也说不清为什么没有再联系。</p> <p class="soft">你会不会,也想知道——</p> <p class="soft">到底是哪一句话,<br> 让一切结束了。</p> <p class="gap"></p> <p>我没有给你答案。</p> <p class="soft">因为很多事情,本来就没有答案。</p> <p class="gap"></p> <p>谢谢你玩到这里。</p> <p class="whisper">谢谢你,把这几分钟,<br> 给了这段没有结果的青春。</p> <p class="sign">—— 作者</p> </div> <div class="afterword-actions"> <button id="afterword-restart" class="afterword-btn">重新开始</button> <button id="afterword-support" class="afterword-btn support">支持作者 ☕</button> </div> </div> <!-- 收款码弹窗 --> <div id="qr-modal" class="qr-modal" aria-hidden="true"> <div class="qr-modal-box"> <div class="qr-modal-title">请我喝一杯咖啡</div> <div class="qr-modal-sub">如果这个故事陪你走过了一小段路</div> <img id="qr-img" class="qr-img" src="【把你的收款码图床直链放这里】" alt="收款码" draggable="false"> <div class="qr-modal-hint">长按或截图后扫码</div> <button id="qr-modal-close" class="qr-modal-close">关闭</button> </div> </div> <style> .afterword-screen { min-height: 78vh; display: flex; flex-direction: column; align-items: center; justify-content: center; padding: 60px 24px 40px; text-align: center; font-family: "Songti SC", "STSong", "SimSun", "Noto Serif SC", serif; color: #c8c8d4; animation: awFade 2s ease both; } @keyframes awFade { from { opacity: 0; } to { opacity: 1; } } .afterword-title { font-size: 13px; letter-spacing: .55em; text-indent: .55em; color: #6a6a80; margin-bottom: 42px; animation: awRise 1.8s ease both; } /* ====== 关键:正文居中 ====== */ .afterword-body { max-width: 480px; margin: 0 auto; /* 水平居中 */ font-size: 15px; line-height: 2.1; letter-spacing: .1em; color: #c4c4d2; text-align: center; /* ← 原来是 left,改成 center */ } .afterword-body p { margin: 0 0 18px; animation: awRise 1.6s ease both; text-align: center; /* 双重保险 */ } .afterword-body p:nth-child(1) { animation-delay: 1.0s; } .afterword-body p:nth-child(2) { animation-delay: 1.8s; } .afterword-body p:nth-child(3) { animation-delay: 2.6s; } .afterword-body p:nth-child(4) { animation-delay: 3.4s; } .afterword-body p:nth-child(5) { animation-delay: 4.2s; } .afterword-body p:nth-child(6) { animation-delay: 4.8s; } .afterword-body p:nth-child(7) { animation-delay: 5.4s; } .afterword-body p:nth-child(8) { animation-delay: 6.0s; } .afterword-body p:nth-child(9) { animation-delay: 6.8s; } .afterword-body p:nth-child(10){ animation-delay: 7.6s; } .afterword-body p:nth-child(11){ animation-delay: 8.4s; } .afterword-body p:nth-child(12){ animation-delay: 9.2s; } .afterword-body .soft { color: #8a8a98; } .afterword-body .whisper { color: #6a6a78; font-size: 13px; font-style: italic; } .afterword-body .sign { color: #8a8a98; text-align: center; margin-top: 24px; letter-spacing: .3em; text-indent: .3em; } .afterword-body .gap { height: 8px; margin: 0; } @keyframes awRise { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: translateY(0); } } .afterword-actions { margin-top: 54px; display: flex; gap: 14px; flex-wrap: wrap; justify-content: center; animation: awRise 1s ease both; animation-delay: 10.2s; } .afterword-btn { padding: 12px 34px; background: transparent; border: 1px solid #3a3a48; color: #8a8a98; font-family: inherit; font-size: 13px; letter-spacing: .35em; text-indent: .35em; cursor: pointer; border-radius: 2px; transition: color .4s ease, border-color .4s ease, box-shadow .4s ease, background .4s ease; } .afterword-btn:hover { color: #e8e8f2; border-color: #6a6a78; box-shadow: 0 0 22px rgba(180, 180, 220, 0.14); } .afterword-btn.support { border-color: #6a5030; color: #b89a70; } .afterword-btn.support:hover { color: #e0c090; border-color: #a07840; box-shadow: 0 0 22px rgba(200, 160, 80, 0.16); } .qr-modal { position: fixed; inset: 0; background: rgba(0, 0, 0, 0.82); display: none; align-items: center; justify-content: center; z-index: 9999; padding: 24px; backdrop-filter: blur(4px); -webkit-backdrop-filter: blur(4px); } .qr-modal.show { display: flex; animation: qrFade .4s ease both; } @keyframes qrFade { from { opacity: 0; } to { opacity: 1; } } .qr-modal-box { background: #14141b; border: 1px solid #2a2a38; border-radius: 10px; padding: 36px 40px 28px; max-width: 340px; width: 100%; text-align: center; font-family: "Songti SC", "STSong", "SimSun", "Noto Serif SC", serif; box-shadow: 0 24px 80px rgba(0, 0, 0, 0.7); animation: qrRise .55s cubic-bezier(.2,.8,.2,1) both; animation-delay: .1s; } @keyframes qrRise { from { opacity: 0; transform: translateY(16px) scale(.97); } to { opacity: 1; transform: translateY(0) scale(1); } } .qr-modal-title { font-size: 16px; letter-spacing: .25em; text-indent: .25em; color: #e8e8f2; margin-bottom: 10px; } .qr-modal-sub { font-size: 13px; color: #8a8a98; letter-spacing: .08em; margin-bottom: 24px; } .qr-img { display: block; width: 100%; max-width: 240px; margin: 0 auto 16px; border-radius: 6px; background: #fff; padding: 8px; } .qr-modal-hint { font-size: 12px; color: #6a6a78; letter-spacing: .1em; margin-bottom: 22px; } .qr-modal-close { padding: 8px 28px; background: transparent; border: 1px solid #3a3a48; color: #8a8a98; font-family: inherit; font-size: 12px; letter-spacing: .3em; text-indent: .3em; cursor: pointer; border-radius: 2px; transition: color .35s ease, border-color .35s ease; } .qr-modal-close:hover { color: #e8e8f2; border-color: #6a6a78; } </style> <script> $(function(){ $("#afterword-restart").on("click", function(){ $(".afterword-screen").css({ transition: "opacity 1s ease, transform 1s ease", opacity: 0, transform: "translateY(8px)" }); setTimeout(function(){ SugarCube.Engine.play("序章"); }, 1050); }); $("#afterword-support").on("click", function(){ $("#qr-modal").addClass("show").attr("aria-hidden", "false"); }); $("#qr-modal-close").on("click", function(){ $("#qr-modal").removeClass("show").attr("aria-hidden", "true"); }); $("#qr-modal").on("click", function(e){ if (e.target === this) { $(this).removeClass("show").attr("aria-hidden", "true"); } }); $(document).on("keydown.qrEsc", function(e){ if (e.which === 27) { $("#qr-modal").removeClass("show").attr("aria-hidden", "true"); } }); }); </script>