Typecho VOID 个人优化方案
今天是七夕,但不是因为没有对象才伤心的。
什么年代还用传统VOID🤣
太没实力了直接给我🪑👇
首页瀑布流文章圆角
在VOID主css找到 section#index-list>ul>li
往里面加
border-radius: 1.5rem;overflow: hidden;
右键复制弹窗
oncopy加个sweetalert2就完事了,任何网页都可以使用。 打开主题的文件夹找到includes\footer.php
往最下面那一滑在 </body>
的<?php endif; ?>
前加入如下代码
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@8"></script>
<script>document.body.oncopy = function(){Swal.fire({allowOutsideClick:false,type:'success',title: '复制成功,如转载请注明出处!',showConfirmButton: false,timer: 2000});};</script>
圆点小指针
在Xecades大佬的这篇文章学来的,但是实际对VOID的使用观感并不好,后来就没有用。
cursor.css
#cursor {
position: fixed;
width: 16px;
height: 16px;
background: #694d4d;
border-radius: 8px;
opacity: 0.25;
z-index: 10086;
pointer-events: none;
transition: 0.2s ease-in-out;
transition-property: background, opacity, transform;
}
#cursor.hidden {
opacity: 0;
}
#cursor.hover {
opacity: 0.1;
transform: scale(2.5);
}
#cursor.active {
opacity: 0.5;
transform: scale(0.5);
}
cursor.js
var CURSOR;
Math.lerp = (a, b, n) => (1 - n) * a + n * b;
const getStyle = (el, attr) => {
try {
return window.getComputedStyle
? window.getComputedStyle(el)[attr]
: el.currentStyle[attr];
} catch (e) { }
return "";
};
class Cursor {
constructor() {
this.pos = { curr: null, prev: null };
this.pt = [];
this.create();
this.init();
this.render();
}
move(left, top) {
this.cursor.style["left"] = `${left}px`;
this.cursor.style["top"] = `${top}px`;
}
create() {
if (!this.cursor) {
this.cursor = document.createElement("div");
this.cursor.id = "cursor";
this.cursor.classList.add("hidden");
document.body.append(this.cursor);
}
var el = document.getElementsByTagName('*');
for (let i = 0; i < el.length; i++)
if (getStyle(el[i], "cursor") == "pointer")
this.pt.push(el[i].outerHTML);
document.body.appendChild((this.scr = document.createElement("style")));
this.scr.innerHTML = `* {cursor: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8' width='8px' height='8px'><circle cx='4' cy='4' r='4' opacity='.5'/></svg>") 4 4, auto !important}`;
}
refresh() {
this.scr.remove();
this.cursor.classList.remove("hover");
this.cursor.classList.remove("active");
this.pos = { curr: null, prev: null };
this.pt = [];
this.create();
this.init();
this.render();
}
init() {
document.onmouseover = e => this.pt.includes(e.target.outerHTML) && this.cursor.classList.add("hover");
document.onmouseout = e => this.pt.includes(e.target.outerHTML) && this.cursor.classList.remove("hover");
document.onmousemove = e => { (this.pos.curr == null) && this.move(e.clientX - 8, e.clientY - 8); this.pos.curr = { x: e.clientX - 8, y: e.clientY - 8 }; this.cursor.classList.remove("hidden"); };
document.onmouseenter = e => this.cursor.classList.remove("hidden");
document.onmouseleave = e => this.cursor.classList.add("hidden");
document.onmousedown = e => this.cursor.classList.add("active");
document.onmouseup = e => this.cursor.classList.remove("active");
}
render() {
if (this.pos.prev) {
this.pos.prev.x = Math.lerp(this.pos.prev.x, this.pos.curr.x, 0.15);
this.pos.prev.y = Math.lerp(this.pos.prev.y, this.pos.curr.y, 0.15);
this.move(this.pos.prev.x, this.pos.prev.y);
} else {
this.pos.prev = this.pos.curr;
}
requestAnimationFrame(() => this.render());
}
}
(() => {
CURSOR = new Cursor();
})();
F12提示条
snackbar.css
/* Snackbar */
.snackbar-container {
background: #425AEF!important;
color: #fff!important;
border-radius: 0!important;
display: flex!important;
justify-content: center!important;
max-width: none!important;
min-width: 100%!important;
margin: 0!important;
left: 0!important;
height: 60px!important;
transform: none!important;
}
@keyframes snackbar-progress{from{width:0}to{width:100%}}
.snackbar-container:after {
position: absolute;
width: 0;
height: 100%;
left: 0;
top: 0;
background: #fff;
opacity: .1;
content: "";
animation: snackbar-progress var(--snackbar-time) linear forwards;
pointer-events: none;
}
core.js
var utils = {
snackbarShow: function(e, t, n) {
var r = void 0 !== t && t,
o = void 0 !== n ? n : 5e3,
i = "top-center",
a = "light" === document.documentElement.getAttribute("data-theme") ? "#49b1f5" : "#121212";
document.styleSheets[0].addRule(":root", "--snackbar-time:" + o + "ms!important"), Snackbar.show({ text: e, backgroundColor: a, showAction: r, duration: o, pos: i })
}
}
window.onkeydown = function(e) { 123 === e.keyCode && utils.snackbarShow("开发者模式已打开,请遵循GPL协议", !1, 3e3) };
snackbar.js
/*!
* Snackbar v0.1.14
* http://polonel.com/Snackbar
*
* Copyright 2018 Chris Brame and other contributors
* Released under the MIT license
* https://github.com/polonel/Snackbar/blob/master/LICENSE
*/
(function(root, factory) {
'use strict';
if (typeof define === 'function' && define.amd) {
define([], function() {
return (root.Snackbar = factory());
});
} else if (typeof module === 'object' && module.exports) {
module.exports = root.Snackbar = factory();
} else {
root.Snackbar = factory();
}
})(this, function() {
var Snackbar = {};
Snackbar.current = null;
var $defaults = {
text: 'Default Text',
textColor: '#FFFFFF',
width: 'auto',
showAction: true,
actionText: 'Dismiss',
actionTextAria: 'Dismiss, Description for Screen Readers',
alertScreenReader: false,
actionTextColor: '#4CAF50',
showSecondButton: false,
secondButtonText: '',
secondButtonAria: 'Description for Screen Readers',
secondButtonTextColor: '#4CAF50',
backgroundColor: '#323232',
pos: 'bottom-left',
duration: 5000,
customClass: '',
onActionClick: function(element) {
element.style.opacity = 0;
},
onSecondButtonClick: function(element) {},
onClose: function(element) {}
};
Snackbar.show = function($options) {
var options = Extend(true, $defaults, $options);
if (Snackbar.current) {
Snackbar.current.style.opacity = 0;
setTimeout(
function() {
var $parent = this.parentElement;
if ($parent)
// possible null if too many/fast Snackbars
$parent.removeChild(this);
}.bind(Snackbar.current),
500
);
}
Snackbar.snackbar = document.createElement('div');
Snackbar.snackbar.className = 'snackbar-container ' + options.customClass;
Snackbar.snackbar.style.width = options.width;
var $p = document.createElement('p');
$p.style.margin = 0;
$p.style.padding = 0;
$p.style.color = options.textColor;
$p.style.fontSize = '18px';
$p.style.fontWeight = 300;
$p.style.lineHeight = '1em';
$p.innerHTML = options.text;
Snackbar.snackbar.appendChild($p);
Snackbar.snackbar.style.background = options.backgroundColor;
if (options.showSecondButton) {
var secondButton = document.createElement('button');
secondButton.className = 'action';
secondButton.innerHTML = options.secondButtonText;
secondButton.setAttribute('aria-label', options.secondButtonAria);
secondButton.style.color = options.secondButtonTextColor;
secondButton.addEventListener('click', function() {
options.onSecondButtonClick(Snackbar.snackbar);
});
Snackbar.snackbar.appendChild(secondButton);
}
if (options.showAction) {
var actionButton = document.createElement('button');
actionButton.className = 'action';
actionButton.innerHTML = options.actionText;
actionButton.setAttribute('aria-label', options.actionTextAria);
actionButton.style.color = options.actionTextColor;
actionButton.addEventListener('click', function() {
options.onActionClick(Snackbar.snackbar);
});
Snackbar.snackbar.appendChild(actionButton);
}
if (options.duration) {
setTimeout(
function() {
if (Snackbar.current === this) {
Snackbar.current.style.opacity = 0;
// When natural remove event occurs let's move the snackbar to its origins
Snackbar.current.style.top = '-100px';
Snackbar.current.style.bottom = '-100px';
}
}.bind(Snackbar.snackbar),
options.duration
);
}
if (options.alertScreenReader) {
Snackbar.snackbar.setAttribute('role', 'alert');
}
Snackbar.snackbar.addEventListener(
'transitionend',
function(event, elapsed) {
if (event.propertyName === 'opacity' && this.style.opacity === '0') {
if (typeof(options.onClose) === 'function')
options.onClose(this);
this.parentElement.removeChild(this);
if (Snackbar.current === this) {
Snackbar.current = null;
}
}
}.bind(Snackbar.snackbar)
);
Snackbar.current = Snackbar.snackbar;
document.body.appendChild(Snackbar.snackbar);
var $bottom = getComputedStyle(Snackbar.snackbar).bottom;
var $top = getComputedStyle(Snackbar.snackbar).top;
Snackbar.snackbar.style.opacity = 1;
Snackbar.snackbar.className =
'snackbar-container ' + options.customClass + ' snackbar-pos ' + options.pos;
};
Snackbar.close = function() {
if (Snackbar.current) {
Snackbar.current.style.opacity = 0;
}
};
// Pure JS Extend
// http://gomakethings.com/vanilla-javascript-version-of-jquery-extend/
var Extend = function() {
var extended = {};
var deep = false;
var i = 0;
var length = arguments.length;
if (Object.prototype.toString.call(arguments[0]) === '[object Boolean]') {
deep = arguments[0];
i++;
}
var merge = function(obj) {
for (var prop in obj) {
if (Object.prototype.hasOwnProperty.call(obj, prop)) {
if (deep && Object.prototype.toString.call(obj[prop]) === '[object Object]') {
extended[prop] = Extend(true, extended[prop], obj[prop]);
} else {
extended[prop] = obj[prop];
}
}
}
};
for (; i < length; i++) {
var obj = arguments[i];
merge(obj);
}
return extended;
};
return Snackbar;
});
顶栏小图标
这个直接动了header.php
把原来的顶栏链接改成了自己带图标的链接👉👈 以后还得手动改,没有一劳永逸的方法👉👈
调用好fontawesome、remixicon、feathericons这种图标包之后在
<span class="dropdown">
或者其他a标签内加图标代码即可
首页瀑布流文章页头一言
在主题文件夹下的
includes\banner.php
的107行左右<div align="center" class="container wide">
插入如下代码
<style>.headline{color:#999;font-size:1.3em;font-weight:300;margin:1.5em 0;position:relative;padding:25px 0;text-align:center;border-top:1px solid rgba(0,0,0,.05);border-bottom:1px solid rgba(0,0,0,.05)}.theme-dark .headline{border-color:rgba(255,255,255,.05)}</style><p id="hitokoto" class="headline" itemprop="headline"><span id="hitokoto_text">:D 让子弹飞一会儿</span></p><script src="https://v1.hitokoto.cn/?encode=js&select=%23hitokoto&c=i" defer></script>
如果你是个精神洁癖,发现首页一言不支持免刷新加载,请在后台主题设置
PJAX 重载函数
中加入
fetch('https://v1.hitokoto.cn')
.then(response => response.json())
.then(data => {
const hitokoto = document.querySelector('#hitokoto_text')
hitokoto.innerText = data.hitokoto
})
.catch(console.error)
如果你是个重度精神洁癖,发现不在首页时console就会报pjax代码中找不到一言函数的错误时
请不要惊慌,无论你怎么百度怎么if page=index
都还是会报错的
解决方法一:当你打开f12时,刺瞎你的眼睛。
解决方法二:在footer也加一个
hitokoto_text
,当你的眼睛移到footer时刺瞎你的眼睛。
解决方法三:只需要在
footer 标签输出内容
中插入
<div id="hitokoto_text" style="display: none;"> </div>
你看,刺瞎这个在footer的hitokoto的眼睛显然要好一点😘
文章标题下信息美化
我不太喜欢VOID带的这个请注意,本文编写于 * 天前,最后修改于 * 天前,其中某些信息可能已经过时
,所以我直接把修改日期搬到了标题下信息栏。
依然还是在
includes\banner.php
大约68行左右找到以下代码
<span><a href="<?php $this->author->permalink(); ?>"><?php $this->author(); ?></a></span> •
<time datetime="<?php echo date('c', $this->created); ?>"><?php echo date('Y-m-d', $this->created); ?></time>
• <a no-pjax target="_self" href="javascript:void(0);" onclick="VOID_SmoothScroller.scrollTo('#comments', -60)"><?php $this->commentsNum(); ?> 评论</a>
<?php if($setting['VOIDPlugin']) echo ' • <span>'.$this->viewsNum.' 阅读</span>'; ?>
替换成
<span>
<i class="ri-rest-time-line" style="padding-right: 5px;"></i> <time datetime="<?php echo date('c', $this->created); ?>"><?php echo date('Y-m-d', $this->created); ?></time>
<?php $postCheck = Utils::isOutdated($this); if($postCheck["is"] && $this->is('post')): ?> <?php endif; ?>
• <i class="ri-file-warning-line" style="padding-right: 5px;"></i> 修改于 <?php echo $postCheck["updated"]; ?> 天前
• <i class="ri-discuss-line" style="padding-right: 5px;"></i> <a no-pjax target="_self" href="javascript:void(0);" onclick="VOID_SmoothScroller.scrollTo('#comments', -60)"><?php $this->commentsNum(); ?> 评论</a>
<?php if($setting['VOIDPlugin']) echo ' • <span> <i class="ri-focus-3-line" style="padding-right: 5px;"></i>'.$this->viewsNum.' 阅读</span>'; ?>
替换的前提是你的网站已经使用了remixicon,如需修改图标或者舍弃图标请针对代码内的
i标签
进行修订。
菜单栏随机文章按钮
泽泽大佬的思路
$db = Typecho_Db::get(); $result = $db->fetchRow($db->select()->from('table.contents')->where('type=?', 'post')->where('status=?', 'publish')->limit(1)->order('RAND()')); if($result) { $f=Helper::widgetById('Contents',$result['cid']); $permalink = $f->permalink; header('Location: '.$permalink); }
只需要给他加一个判断的条件,放到
includes\functions.php
33行左右function themeInit()
下
if ($_GET['plugin']=='ramdom') {
$db = Typecho_Db::get();
$result = $db->fetchRow($db->select()->from('table.contents')->where('type=?', 'post')->where('status=?', 'publish')->limit(1)->order('RAND()'));
if($result) {
$f=Helper::widgetById('Contents',$result['cid']);
$permalink = $f->permalink;
header('Location: '.$permalink);
}
}
再在同目录的
footer.php
33行左右<div class="ctrler-item" id="go-top">
的闭合标签</div>
下插入
<div class="ctrler-item hidden-xs">
<a aria-label="进入后台" href="https://domain/?plugin=ramdom" style="transform: translateX(-2px);"><i class="ri-gift-fill"></i></a>
替换的前提是你的网站已经使用了remixicon,如需修改图标或者舍弃图标请针对代码内的
i标签
进行修订。
瀑布流文章时间侧加入文章类目
模板文件夹下
index.php
75行左右替换<span class="word-count">+ <?php echo $this->wordCount; ?> 字</span>
至
<span class="word-count">+ <?php echo $this->wordCount; ?> 字</span> #<?php echo $this->categories[0]['name']; ?>#
第一次访问网站弹出公告
快说谢谢火喵酱
notice.css
.annotice {
box-shadow: 0 .25rem .5rem rgba(0,0,0,.05),0 1.5rem 2.2rem rgba(0,0,0,.1)!important;
padding: 0 30px;
background: #fff;
width: 400px;
/*height: 470px;*/
position: fixed;
top: 50%;
left: 50%;
z-index: 999999;
transform: translateX(-50%) translateY(-50%);
margin: 0 auto;
border-radius: 18px;
}
.annotice-header {
color: white;
line-height: 40px;
text-align: center;
}
.annotice-main {
padding: 20px;
}
.annotice-main p {
padding: 12px 0 0;
}
.annotice-footer {
padding: 10px 0 30px;
text-align: center;
}
.annotice-title {
position: relative;
font-size: 18px;
font-weight: 900;
display: inline-block;
color:#fff;
}
.annotice strong {
color: #46A3FF;
}
.annotice-btn {
font-weight: 700;
border-radius: 50px;
width: 100%;
cursor: pointer;
background: #46A3FF;
color: #fff;
padding: 0 15px;
line-height: 40px;
font-size: 14px;
display: inline-block;
}
.annotice::after {
content: '';
height: 80px;
width: 100%;
background: #46A3FF;
position: absolute;
top: 0;
left: 0;
z-index: -1;
border-radius: 18px 18px 0 0;
}
修改好下面的内容,在你喜欢的地方狠狠的插入他们🤩
<div class="annotice" id="annotice-note" style="display: none;">
<div class="annotice-header">
<h3 class="annotice-title"> 『 给初次见面的你的一封信 』 </h3>
</div>
<div class="annotice-main">
<p>嘿,<br>欢迎到访我的博客!</p><p>我的小博客里什么都有,但是又好像什么都没有。我已经<strong>尽力</strong>让它看起来五彩斑斓许多。这里有我的回忆、我的经历、我的见解和我的探讨,希望你在这里可以让你的知识面<strong>焕然一新</strong>!</p><p>我陪我的小博客走过的快三年啦,最近我给它换了个新名字“<strong>低潮鲸鸣</strong>”,所以以前的朋友记住要忘掉它的老名字“<strong>十月寒冬</strong>”!</p><p>我很喜欢浓厚的学术讨论氛围,所以你在评论区发表的见解一定要<strong>友善一点点</strong>,不然我会把不听话的小坏蛋<strong>打进垃圾箱</strong>!当然如果你想和我说点小秘密,header 里面有我的联系方式。只有<strong>电子邮箱</strong>是我最经常使用的联系方式,除此之外,一切随缘。</p><p>嘛,就暂且说这么多了,以后再加!</p>
</div>
<div class="annotice-footer"><span class="annotice-btn" onclick="closeclick()">好哒</span></div>
</div>
<script>
$(function(){
if(window.localStorage.getItem("isClose") == 'yes'){
$('#annotice-note')[0].style.display='none';
}else
{
$('#annotice-note')[0].style.display='block';
}
});
function closeclick()
{
$('#annotice-note')[0].style.display='none';
window.localStorage.setItem("isClose", "yes");
}
</script>
404页面
仅提供个定位,
includes\banner.php
58行处的<span class="glitch">0</span>
加入TianliGPT自动生成文章的AI摘要
具体情况可以看洪佬的文章,VOID的元素属性的选择器是.articleBody
,即
let tianliGPT_postSelector = ‘.articleBody’;
欲想使之支持VOID的夜间模式,请将我不成熟的css加入到TianliGPT的调用css中(css和js都可以自行保存调用)
.theme-dark .post-TianliGPT {
background: #21232A;
border-radius: 12px;
padding: 12px;
line-height: 1.3;
border: var(--style-border-always);
margin: 16px 0;
}
.theme-dark .tianliGPT-title {
display: flex;
color: #6e6e6e;
border-radius: 8px;
align-items: center;
padding: 0 12px;
cursor: default;
user-select: none;
}
.theme-dark .tianliGPT-explanation {
margin-top: 12px;
padding: 8px 12px;
background: #21232A;
border-radius: 8px;
border: var(--style-border-always);
font-size: 15px;
line-height: 1.4;
display: flex;
}
.theme-dark .tianliGPT-tag {
font-size: 12px;
background-color: #6e6e6e;
color: var(--heo-card-bg);
font-weight: bold;
border-radius: 4px;
margin-left: auto;
line-height: 1;
padding: 4px;
display: flex;
align-items: center;
justify-content: center;
transition: 0.3s;
}
VOID评论后台IP为IPV6时报错
官方的解决方案是识别为ipv6就返回N/A
,这个问题我最早遇到,Given直接动刀文件夹插件下的libs\IP.php
,调用了IPIP的GeoLite2-City.mmdb
数据库,实现了IPV6地址识别。 数据库就不提供了,Github可以找得到。
IP.php
<?php
require_once 'vendor/autoload.php';
use GeoIp2\Database\Reader;
class IPLocation_IP
{
private static $ip = NULL;
private static $fp = NULL;
private static $offset = NULL;
private static $index = NULL;
private static $cached = array();
public static function find($ip)
{
if (empty($ip) === TRUE)
{
return 'N/A';
}
$nip = gethostbyname($ip);
$ipdot = explode('.', $nip);
if ($ipdot[0] < 0 || $ipdot[0] > 255 || count($ipdot) !== 4)
{
return 'N/A';
}
if (isset(self::$cached[$nip]) === TRUE)
{
return self::$cached[$nip];
}
if (self::$fp === NULL)
{
self::init();
}
$nip2 = pack('N', ip2long($nip));
$tmp_offset = (int)$ipdot[0] * 4;
$start = unpack('Vlen', self::$index[$tmp_offset] . self::$index[$tmp_offset + 1] . self::$index[$tmp_offset + 2] . self::$index[$tmp_offset + 3]);
$index_offset = $index_length = NULL;
$max_comp_len = self::$offset['len'] - 1024 - 4;
for ($start = $start['len'] * 8 + 1024; $start < $max_comp_len; $start += 8)
{
if (self::$index{$start} . self::$index{$start + 1} . self::$index{$start + 2} . self::$index{$start + 3} >= $nip2)
{
$index_offset = unpack('Vlen', self::$index{$start + 4} . self::$index{$start + 5} . self::$index{$start + 6} . "\x0");
$index_length = unpack('Clen', self::$index{$start + 7});
break;
}
}
if ($index_offset === NULL)
{
return 'N/A';
}
fseek(self::$fp, self::$offset['len'] + $index_offset['len'] - 1024);
self::$cached[$nip] = explode("\t", fread(self::$fp, $index_length['len']));
return self::$cached[$nip];
}
private static function init()
{
if (self::$fp === NULL)
{
self::$ip = new self();
self::$fp = fopen(__DIR__ . '/17monipdb.dat', 'rb');
if (self::$fp === FALSE)
{
throw new Exception('Invalid 17monipdb.dat file!');
}
self::$offset = unpack('Nlen', fread(self::$fp, 4));
if (self::$offset['len'] < 4)
{
throw new Exception('Invalid 17monipdb.dat file!');
}
self::$index = fread(self::$fp, self::$offset['len'] - 4);
}
}
public function __destruct()
{
if (self::$fp !== NULL)
{
fclose(self::$fp);
}
}
public static function locate ($ip)
{
$reader = new Reader('/wwwroot/GeoLite2-City.mmdb的绝对路径');
if($ip != '127.0.0.1'){
$record = $reader->city($ip);
$addresses = IPLocation_IP::find($ip);
if($addresses == "N/A"){
$addresses = $record->city->name;
if($addresses == null){
$addresses = $record->country->names['zh-CN'];
}
}
if (is_array($addresses)) {
$addresses = array_unique($addresses);
$addresses = implode('', $addresses);
}
}
else{
$addresses = "未知或保留地址";
}
#return $address;
#echo($addresses);
return $addresses;
}
}
我推的模块😍
好好好,喜欢变形记是吧
BracketDown
BracketDown 是一个适用于 Typecho 的 Markdown 语法拓展插件,加入了一系列短代码,方便博主在编写文章时更好地排版。 BigCoke233/typecho-plugin-BracketDown
assets\default.css
VOID夜间模式css.theme-dark .bracketdown-post-title a { color: #666; border: none; text-decoration: none } .theme-dark .bracketdown-post-meta a { border: none; text-decoration: none; color: #666; display: inline-block; font-size: .9em; border: 1px solid #222; padding: 0 .5em; transition-property: border, background, color; transition-duration: .3s } .theme-dark .bracketdown-post-meta a:hover { border-color: #222; background: #222; color: #fff }
ShortLinks
外链转内链,对seo有奇效(应该
And so on..
剩下的都挺好用,没有啥好说的,放个图吧