Acrylic 主题自定义七:自定义鼠标右键菜单

JS 自定义鼠标右键菜单主要是利用鼠标右键点击事件(contextmenu) 来实现

主题配置

在主题配置文件(_config.Acrylic.yml) 中新增配置

1
2
# 右键菜单自定义
rightMenu: true

菜单结构

themes/Acrylic/layout/partial 文件夹下新建 rightMenu.ejs,代码如下👇

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
<div id="right-menu">
<ul class="right-menu-nav">
<li class="right-menu-nav-item" onclick="history.back()">
<a title="返回">
<i class="fas fa-arrow-left"></i>
</a>
</li>
<li class="right-menu-nav-item" onclick="history.forward()">
<a title="前进">
<i class="fas fa-arrow-right"></i>
</a>
</li>
<li class="right-menu-nav-item" onclick="location.reload()">
<a title="刷新">
<i class="fas fa-redo"></i>
</a>
</li>
<li class="right-menu-nav-item" onclick="acrylic.toTop()">
<a title="回到顶部">
<i class="fas fa-arrow-up"></i>
</a>
</li>
</ul>
<hr>
<div id="right-menu-img">
<ul class="right-menu-content">
<li id="img-copy" class="right-menu-item">
<a title="复制图片">
<i class="fas fa-copy"></i>
<span>复制图片</span>
</a>
</li>
<li id="img-copy-link" class="right-menu-item">
<a title="复制链接">
<i class="fas fa-link"></i>
<span>复制链接</span>
</a>
</li>
<li id="img-view" class="right-menu-item">
<a title="新窗口查看">
<i class="fas fa-eye"></i>
<span>新窗口查看</span>
</a>
</li>
<li id="img-google-search" class="right-menu-item">
<a title="谷歌搜图">
<i class="fas fa-search"></i>
<span>谷歌搜图</span>
</a>
</li>
</ul>
</div>
<div id="right-menu-link">
<ul class="right-menu-content">
<li id="jump-link" class="right-menu-item">
<a title="跳转链接">
<i class="fas fa-link"></i>
<span>跳转链接</span>
</a>
</li>
<li id="copy-link" class="right-menu-item">
<a title="复制链接">
<i class="fas fa-copy"></i>
<span>复制链接</span>
</a>
</li>
</ul>
</div>
<div id="right-menu-post">
<ul class="right-menu-content">
<li class="right-menu-item" onclick="toRandomPost()">
<a title="随便看看">
<i class="fas fa-shuffle"></i>
<span>随便看看</span>
</a>
</li>
<li class="right-menu-item">
<a title="博客分类" href="/categories">
<i class="fas fa-cube"></i>
<span>博客分类</span>
</a>
</li>
<li class="right-menu-item">
<a title="文章标签" href="/tags">
<i class="fas fa-tags"></i>
<span>文章标签</span>
</a>
</li>
<li class="right-menu-item" onclick="acrylic.copyPageUrl()">
<a title="复制地址">
<i class="fas fa-copy"></i>
<span>复制地址</span>
</a>
</li>
</ul>
</div>
<hr>
<ul class="right-menu-content">
<li id="right-menu-theme" class="right-menu-item" onclick="acrylic.switchDarkMode()">
<a title="">
<i class="fas"></i>
<span></span>
</a>
</li>
<li id="right-menu-comment" class="right-menu-item" onclick="acrylic.toComment()">
<a title="直达评论">
<i class="fas fa-message"></i>
<span>直达评论</span>
</a>
</li>
</ul>
</div>

themes/Acrylic/layout/layout.ejs 中引入,需在引入 partial/body 之前引入

1
2
3
4
<% if(theme.rightMenu){ %>
<%- partial('partial/rightMenu', {cache: true}) %>
<% } %>
<%- partial('partial/body', {cache: true}) %>

菜单样式

themes/Acrylic/source/css 文件夹下新建 right-menu.css,代码如下👇

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
#right-menu {
position: fixed;
z-index: 999999;
user-select: none;
background: var(--ruins-card-bg);
box-shadow: var(--ruins-shadow-main);
border: var(--style-border);
max-width: 240px;
overflow: hidden;
border-radius: 6px;
padding: 0.35rem 0;
transition: 0.3s;
}

#right-menu:hover {
border: var(--style-border-hover);
box-shadow: var(--ruins-shadow-theme);
}

#right-menu .right-menu-nav {
display: flex;
justify-content: space-between;
text-align: center;
line-height: 32px;
margin: 0 0.35rem;
transition: 0.3s;
}

#right-menu .right-menu-nav-item {
width: 32px;
height: 32px;
margin: 0;
padding: 0;
overflow: hidden;
border-radius: 50%;
margin: 0 0.1rem;
transition: 0.3s;
}

#right-menu .right-menu-nav-item > a {
cursor: pointer;
transition: 0.3s;
}

#right-menu .right-menu-nav-item:hover {
background-color: var(--ruins-main);
box-shadow: var(--ruins-shadow-main);
}

#right-menu .right-menu-nav-item:hover a {
color: var(--ruins-white);
}

#right-menu hr {
margin: 8px auto;
}

#right-menu .right-menu-content {
margin: 0 0.35px;
transition: 0.3s;
}

#right-menu .right-menu-item {
margin: 0.35rem;
}

#right-menu .right-menu-item > a {
display: block;
width: 100%;
border-radius: 8px;
transition: 0.3s;
padding: 0.25rem;
cursor: pointer;
}

#right-menu .right-menu-item:hover > a {
color: var(--ruins-white);
background-color: var(--ruins-main);
box-shadow: var(--ruins-shadow-main);
}

#right-menu .right-menu-item > a i {
text-align: center;
width: 32px;
}

themes/Acrylic/layout/partial/head.ejs 中引入

1
2
3
<% if (theme.rightMenu){ %>
<link rel="stylesheet" href="/css/right-menu.css">
<% } %>

菜单逻辑

themes/Acrylic/source/js 文件夹下新建 rightMenu.js,代码如下👇

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
const node = document.querySelector("#right-menu");
const rightMenuImg = document.querySelector("#right-menu-img");
const imgCopy = document.querySelector("#img-copy");
const imgCopyLink = document.querySelector("#img-copy-link");
const imgView = document.querySelector("#img-view");
const imgSearch = document.querySelector("#img-google-search");
const rightMenuPost = document.querySelector("#right-menu-post");
const comment = document.querySelector("#post-comment");
const rightMenuComment = document.querySelector("#right-menu-comment");
const rightMenuLink = document.querySelector("#right-menu-link");
const jumpLink = document.querySelector("#jump-link");
const copyLink = document.querySelector("#copy-link");
let imgSrc = "";
let linkHref = "";

function initRightMenu() {
document.oncontextmenu = function (e) {
const event = e || window.event;
const tagName = event.target.localName;
const pageWidth = window.innerWidth;
const pageHeight = window.innerHeight;
const x = event.clientX;
const y = event.clientY;

show(tagName, event);

const menuWidth = node.offsetWidth;
const menuHeight = node.offsetHeight;

node.style = "";
node.style.left =
(x < pageWidth - menuWidth * 2 ? x : x - menuWidth) + "px";
node.style.top = (y < pageHeight - menuHeight ? y : y - menuHeight) + "px";
node.style.display = "block";
return false;
};

document.onclick = function (e) {
if (e.target.id != "right-menu") {
node.style = "";
}
};
}

function show(tagName, event) {
initTheme();
switch (tagName) {
case "img":
imgSrc = event.target.currentSrc;
showImg();
break;
case "a":
linkHref = event.target.href;
showLink();
break;
default:
showPost();
break;
}

addRightMenuClickEvent();
}

function initTheme() {
const themeData = sessionStorage.getItem("theme");
const themeTitle = document.querySelector("#right-menu-theme>a");
const themeIcon = document.querySelector("#right-menu-theme>a>i");
const themeText = document.querySelector("#right-menu-theme>a>span");
if (themeData === "light") {
themeIcon.classList.remove("fa-sun");
themeIcon.className += " fa-moon";
themeTitle.setAttribute("title", "切换黑暗模式");
themeText.innerHTML = "黑暗模式";
} else {
themeIcon.classList.remove("fa-moon");
themeIcon.className += " fa-sun";
themeTitle.setAttribute("title", "切换明亮模式");
themeText.innerHTML = "明亮模式";
}
}

function showImg() {
rightMenuImg.style.display = "block";
rightMenuPost.style.display = "none";
rightMenuLink.style.display = "none";
}

function showLink() {
rightMenuLink.style.display = "block";
rightMenuImg.style.display = "none";
rightMenuPost.style.display = "none";
}

function showPost() {
showComent();
rightMenuImg.style.display = "none";
rightMenuPost.style.display = "block";
rightMenuLink.style.display = "none";
}

function showComent() {
if (comment) {
rightMenuComment.style.display = "block";
} else {
rightMenuComment.style.display = "none";
}
}

function addRightMenuClickEvent() {
imgCopy.addEventListener("click", () => acrylic.copyImage(imgSrc));
imgCopyLink.addEventListener("click", () => acrylic.copyImageLink(imgSrc));
imgView.addEventListener("click", () => window.open(imgSrc));
imgSearch.addEventListener("click", () =>
window.open(`https://www.google.com.hk/searchbyimage?image_url=${imgSrc}`)
);
jumpLink.addEventListener("click", () => window.open(linkHref));
copyLink.addEventListener("click", () => acrylic.copyLink(linkHref));
}

initRightMenu();

themes/Acrylic/layout/partial/body.ejs 中引入

1
2
3
<% if(theme.rightMenu){ %>
<script type="text/javascript" src="/js/rightMenu.js"></script>
<% } %>

功能方法

切换黑暗/明亮模式

原有的切换黑暗/明亮模式功能方法需要进行修改,将右键菜单也进行覆盖

代码文件位置:themes/Acrylic/source/js/main.js

将原有的方法代码替换成下面👇的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
static switchDarkMode() {
let rightMenuThemeTitle = document.querySelector("#right-menu-theme>a");
let rightMenuThemeIcon = document.querySelector("#right-menu-theme>a>i");
let rightMenuThemeText = document.querySelector("#right-menu-theme>a>span");
const nowMode = document.documentElement.getAttribute('data-theme') === 'dark' ? 'dark' : 'light'
if (nowMode === 'light') {
document.documentElement.setAttribute('data-theme', 'dark')
sessionStorage.setItem('theme', 'dark')
utils.snackbarShow(GLOBALCONFIG.lang.theme.dark, false, 2000)
rightMenuThemeIcon.classList.remove("fa-moon");
rightMenuThemeIcon.className += " fa-sun";
rightMenuThemeTitle.setAttribute("title", "切换明亮模式");
rightMenuThemeText.innerHTML = "明亮模式";
} else {
document.documentElement.setAttribute('data-theme', 'light')
sessionStorage.setItem('theme', 'light')
utils.snackbarShow(GLOBALCONFIG.lang.theme.light, false, 2000)
rightMenuThemeIcon.classList.remove("fa-sun");
rightMenuThemeIcon.className += " fa-moon";
rightMenuThemeTitle.setAttribute("title", "切换黑暗模式");
rightMenuThemeText.innerHTML = "黑暗模式";
}
}

直达评论

新增直达评论功能

代码文件位置:themes/Acrylic/source/js/main.js

代码如下👇

1
2
3
4
5
6
7
static toComment() {
window.scrollTo({
top: document.getElementById("post-comment").offsetTop,
left: 0,
behavior: "smooth",
});
}

复制图片

新增复制图片功能

复制图片需要将图片转成二进制数据进行处理,所以我们需要先封装图片转二进制方法

代码文件位置:themes/Acrylic/source/js/utils.js

代码如下👇

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
imageToBlob: (imageURL) => {
const img = new Image();
const c = document.createElement("canvas");
const ctx = c.getContext("2d");
img.crossOrigin = "Anonymous";
img.src = imageURL;
return new Promise((resolve) => {
img.onload = function () {
c.width = this.naturalWidth;
c.height = this.naturalHeight;
ctx.drawImage(this, 0, 0);
c.toBlob(
(blob) => {
resolve(blob);
},
"image/png",
0.75
);
};
});
};

代码文件位置:themes/Acrylic/source/js/main.js

代码如下👇

1
2
3
4
5
6
7
8
9
10
static async copyImage(imageURL) {
try {
const blob = await utils.imageToBlob(imageURL);
const item = new ClipboardItem({ "image/png": blob });
navigator.clipboard.write([item]);
utils.snackbarShow(GLOBALCONFIG.lang.copy.success, false, 2000);
} catch (err) {
utils.snackbarShow(GLOBALCONFIG.lang.copy.error, false, 2000);
}
}

复制链接

新增图片和超链接复制链接的功能

代码文件位置:themes/Acrylic/source/js/main.js

代码如下👇

1
2
3
4
5
6
static copyImageLink(imageURL) {
utils.copy(imageURL);
}
static copyLink(herf) {
utils.copy(herf);
}

文章后期可能会更新