├── .eslintrc ├── .gitignore ├── .stylelintrc.json ├── renovate.json ├── assets ├── dark.css ├── light.css ├── moon-menu.ejs ├── styles.css └── moon-menu.js ├── .vscode └── settings.json ├── package.json ├── index.js ├── README.md └── LICENSE /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "hexo", 3 | "root": true 4 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | *.log 3 | example/ 4 | yarn.lock -------------------------------------------------------------------------------- /.stylelintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "stylelint-config-standard" 3 | } 4 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "config:base", 4 | ":preserveSemverRanges" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /assets/dark.css: -------------------------------------------------------------------------------- 1 | .moon-menu { 2 | --moon-cricle: #2b5278; 3 | --color: #9ab; 4 | --moon-item-bg-color: #182533; 5 | } 6 | -------------------------------------------------------------------------------- /assets/light.css: -------------------------------------------------------------------------------- 1 | .moon-menu { 2 | --moon-cricle: #607d8b; 3 | --color: #fff; 4 | --moon-item-bg-color: #000; 5 | } 6 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "eslint.options": { 3 | "rules": { 4 | "linebreak-style": "off" 5 | } 6 | }, 7 | "editor.codeActionsOnSave": { 8 | "source.fixAll": true 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /assets/moon-menu.ejs: -------------------------------------------------------------------------------- 1 |
2 |
3 | <% menus.forEach(item => { %> 4 |
onclick="<%= item.func %>()"<% } %>> 5 | <%- item.icon -%> 6 |
7 | <% }); %> 8 |
9 |
10 | 11 | 12 | 13 | 14 |
15 |
<%- icon -%>
16 |
17 |
18 |
19 |
-------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hexo-cake-moon-menu", 3 | "version": "2.5.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "p": "yarn --proxy=http://localhost:1080" 9 | }, 10 | "files": [ 11 | "assets", 12 | "index.js" 13 | ], 14 | "repository": { 15 | "type": "git", 16 | "url": "git+https://github.com/jiangtj-lab/hexo-cake-moon-menu.git" 17 | }, 18 | "author": "Mr.J", 19 | "license": "LGPL-3.0", 20 | "bugs": { 21 | "url": "https://github.com/jiangtj-lab/hexo-cake-moon-menu/issues" 22 | }, 23 | "homepage": "https://github.com/jiangtj-lab/hexo-cake-moon-menu#readme", 24 | "dependencies": { 25 | "ejs": "^3.1.6", 26 | "hexo-extend-injector2": "^0.3.0", 27 | "hexo-util": "^2.4.0" 28 | }, 29 | "devDependencies": { 30 | "eslint": "^7.22.0", 31 | "eslint-config-hexo": "^4.1.0", 32 | "stylelint": "^13.12.0", 33 | "stylelint-config-standard": "^21.0.0" 34 | }, 35 | "publishConfig": { 36 | "registry": "https://registry.npmjs.org/" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /assets/styles.css: -------------------------------------------------------------------------------- 1 | .moon-menu, 2 | .moon-menu-bg, 3 | .moon-menu-content { 4 | position: fixed; 5 | right: 20px; 6 | bottom: 20px; 7 | width: 4em; 8 | height: 4em; 9 | z-index: 999; 10 | } 11 | 12 | .moon-menu-button { 13 | width: 4em; 14 | height: 4em; 15 | cursor: pointer; 16 | } 17 | 18 | .moon-menu-cricle { 19 | fill: var(--moon-cricle, #607d8b); 20 | opacity: 0.9; 21 | } 22 | 23 | .moon-menu-border { 24 | stroke: var(--moon-cricle, #607d8b); 25 | opacity: 0.9; 26 | stroke-width: 1px; 27 | fill: none; 28 | transform: rotate(-90deg); 29 | transform-origin: 50% 50%; 30 | } 31 | 32 | .moon-menu-icon { 33 | font-size: 2.5em; 34 | line-height: 1.6em; 35 | color: var(--color, #fff); 36 | text-align: center; 37 | } 38 | 39 | .moon-menu-icon i, 40 | .moon-menu-icon svg { 41 | transition: all 0.15s cubic-bezier(0, 0, 0.382, 1.618); 42 | } 43 | 44 | .moon-menu-icon.active i, 45 | .moon-menu-icon.active svg { 46 | transform: rotate(90deg); 47 | } 48 | 49 | .moon-menu-text { 50 | color: var(--color, #fff); 51 | font-weight: 400; 52 | text-align: center; 53 | line-height: 3.2em; 54 | font-size: 1.2em; 55 | display: none; 56 | } 57 | 58 | .moon-menu-item { 59 | position: absolute; 60 | top: 1em; 61 | left: 1em; 62 | width: 2.5em; 63 | height: 2.5em; 64 | border-radius: 50%; 65 | background-color: var(--moon-item-bg-color, #000); 66 | opacity: 0; 67 | display: block; 68 | transition: all 0.3s cubic-bezier(0, 0, 0.382, 1.618); 69 | z-index: -1; 70 | text-align: center; 71 | line-height: 2.5em; 72 | color: var(--color, #fff); 73 | cursor: pointer; 74 | } 75 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | /* global hexo */ 2 | 'use strict'; 3 | 4 | const { join } = require('path'); 5 | const fs = require('fs'); 6 | const { Cache } = require('hexo-util'); 7 | const injector = require('hexo-extend-injector2')(hexo); 8 | const ejs = require('ejs'); 9 | 10 | const cache = new Cache(); 11 | 12 | hexo.extend.filter.register('after_init', () => { 13 | const faInline = hexo.extend.helper.get('fa_inline'); 14 | const fa = css => { 15 | if (!faInline) { 16 | return ``; 17 | } 18 | const data = css.split(' '); 19 | return faInline(data[1].substring(3), {prefix: data[0]}); 20 | }; 21 | 22 | const config = Object.assign({ 23 | back2top: { 24 | enable: true, 25 | icon: 'fas fa-chevron-up', 26 | order: -1 27 | }, 28 | back2bottom: { 29 | enable: true, 30 | icon: 'fas fa-chevron-down', 31 | order: '-2' 32 | } 33 | }, hexo.config.moon_menu); 34 | 35 | const moonMenuArr = Object.keys(config) 36 | .map(key => { 37 | const val = config[key]; 38 | val.id = val.id || key; 39 | return val; 40 | }) 41 | .map(item => { 42 | item.order = item.order || 0; 43 | if (item.enable === undefined) { 44 | item.enable = true; 45 | } 46 | return item; 47 | }) 48 | .filter(item => item.enable) 49 | .map(item => { 50 | item.icon = fa(item.icon); 51 | return item; 52 | }) 53 | .sort((a, b) => a.order - b.order); 54 | 55 | const light = fs.readFileSync(join(__dirname, 'assets/light.css')).toString(); 56 | const dark = fs.readFileSync(join(__dirname, 'assets/dark.css')).toString(); 57 | injector.register('variable', () => { 58 | return `${light}@media (prefers-color-scheme: dark) {${dark}}`; 59 | }); 60 | injector.register('variable', { 61 | env: 'light', 62 | value: light 63 | }); 64 | injector.register('variable', { 65 | env: 'dark', 66 | value: dark 67 | }); 68 | injector.register('style', join(__dirname, 'assets/styles.css')); 69 | injector.register('js', fs.readFileSync(join(__dirname, 'assets/moon-menu.js')).toString()); 70 | injector.register('bodyEnd', () => { 71 | return cache.apply('cache', () => { 72 | const template = fs.readFileSync(join(__dirname, 'assets/moon-menu.ejs')).toString(); 73 | return ejs.render(template, { icon: fa('fas fa-ellipsis-v'), menus: moonMenuArr }); 74 | }); 75 | }); 76 | }, 1); 77 | -------------------------------------------------------------------------------- /assets/moon-menu.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-var */ 2 | /* global document,window */ 3 | 'use strict'; 4 | ((window, document) => { 5 | 6 | const moonMenuListener = function() { 7 | // Get scroll percent 8 | const offsetHeight = document.documentElement.offsetHeight; 9 | const scrollHeight = document.documentElement.scrollHeight; 10 | const scrollTop = document.documentElement.scrollTop || window.pageYOffset || document.body.scrollTop; 11 | let percent = Math.round(scrollTop / (scrollHeight - offsetHeight) * 100); 12 | if (percent > 100) percent = 100; 13 | 14 | const menuIcon = document.querySelector('.moon-menu-icon'); 15 | const menuText = document.querySelector('.moon-menu-text'); 16 | if (!percent) { 17 | percent = 0; 18 | menuIcon.style.display = 'block'; 19 | menuText.style.display = 'none'; 20 | } else { 21 | menuIcon.style.display = 'none'; 22 | menuText.style.display = 'block'; 23 | menuText.innerHTML = percent + '%'; 24 | } 25 | 26 | // Update strokeDasharray 27 | const length = 196; 28 | document.querySelector('.moon-menu-border').style.strokeDasharray = percent * length / 100 + ' ' + length; 29 | }; 30 | 31 | window.addEventListener('load', () => { 32 | moonMenuListener(); 33 | }); 34 | window.addEventListener('scroll', moonMenuListener); 35 | 36 | document.querySelector('.moon-menu-button').addEventListener('click', () => { 37 | document.querySelector('.moon-menu-icon').classList.toggle('active'); 38 | const items = document.querySelector('.moon-menu-items'); 39 | items.classList.toggle('active'); 40 | const childItems = document.querySelectorAll('.moon-menu-item'); 41 | if (items.classList.contains('active')) { 42 | for (let i = 0; i < childItems.length; i++) { 43 | childItems[i].style.top = -3 - 3 * i + 'em'; 44 | childItems[i].style.opacity = .9; 45 | } 46 | } else { 47 | for (let i = 0; i < childItems.length; i++) { 48 | childItems[i].style.top = '1em'; 49 | childItems[i].style.opacity = 0; 50 | } 51 | } 52 | }); 53 | 54 | const addClickListener = (id, call) => { 55 | const item = document.querySelector(id); 56 | if (item) { 57 | item.addEventListener('click', call); 58 | } 59 | }; 60 | 61 | addClickListener('#moon-menu-item-back2top', () => { 62 | window.scroll({ top: 0, behavior: 'smooth' }); 63 | }); 64 | 65 | addClickListener('#moon-menu-item-back2bottom', () => { 66 | const offsetHeight = document.documentElement.offsetHeight; 67 | const scrollHeight = document.documentElement.scrollHeight; 68 | window.scroll({ top: scrollHeight - offsetHeight, behavior: 'smooth' }); 69 | }); 70 | 71 | })(window, document); 72 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # hexo-cake-moon-menu 2 | 3 | This plugin from [hexo-theme-inside](https://github.com/ike-c/hexo-theme-inside), thank ike-c. 4 | 5 | **If you've come here from my post, check out the [1.x](https://github.com/jiangtj-lab/hexo-cake-moon-menu/tree/1.x) branch code.** 6 | 7 | ![npm](https://img.shields.io/npm/v/hexo-cake-moon-menu.svg) 8 | [![Theme](https://img.shields.io/badge/Theme-NexT(Pisces&Gemini):7.9.0-blue.svg)](https://theme-next.org) 9 | [![Theme](https://img.shields.io/badge/Theme-Cake:2.0.0-blue.svg)](https://github.com/jiangtj/hexo-theme-cake) 10 | 11 | # Preview 12 | ![image](https://user-images.githubusercontent.com/15902347/61098652-41f0ee80-a492-11e9-9c75-bb8fad0aa058.png) 13 | ![image](https://user-images.githubusercontent.com/15902347/61098668-51703780-a492-11e9-984c-a17c1509a4c6.png) 14 | ![image](https://user-images.githubusercontent.com/15902347/61098577-1110b980-a492-11e9-930e-cd0c677f7714.png) 15 | ![image](https://user-images.githubusercontent.com/15902347/61098595-1ff76c00-a492-11e9-8c66-0a702b390961.png) 16 | 17 | ## How to use 18 | 19 | ```bash 20 | yarn add hexo-cake-moon-menu 21 | ``` 22 | 23 | If you are using NexT theme version 7.8 or earlier, install version 2.1.2 24 | 25 | ```bash 26 | yarn add hexo-cake-moon-menu@2.1.2 27 | ``` 28 | 29 | ## Config 30 | 31 | In hexo `_config.yml` (here is default config, if don't change it, nothing need to append) 32 | 33 | ```yml 34 | moon_menu: 35 | back2top: 36 | enable: true 37 | icon: fas fa-chevron-up 38 | order: -1 39 | back2bottom: 40 | enable: true 41 | icon: fas fa-chevron-down 42 | order: -2 43 | ``` 44 | 45 | ## Custom 46 | 47 | It's easy to add new button in `moon-menu`. And here's an example about add gitter sidecar. 48 | 49 | 1. Add config 50 | ```yml 51 | moon_menu: 52 | chat: 53 | icon: fa fa-comments 54 | ``` 55 | 56 | 2. In `${hexo-dir}/scripts/any.js`, Add custom head 57 | ```js 58 | const path = require('path'); 59 | const injector = require('hexo-extend-injector2')(hexo); 60 | injector.register('body-end', ``); 66 | injector.register('body-end', ''); 67 | injector.register('js', path.resolve(hexo.base_dir, 'any/gitter.js')); 68 | ``` 69 | 70 | 3. In `${hexo-dir}/any/gitter.js`, create custom function 71 | ```js 72 | document.addEventListener('gitter-sidecar-instance-started', e => { 73 | // every button has it's id such as #moon-menu-item- 74 | document.querySelector('#moon-menu-item-chat').addEventListener('click', () => { 75 | e.detail.chat.toggleChat(true); 76 | }); 77 | }); 78 | ``` 79 | 80 | ## Other themes 81 | 82 | If you're not a user of the NexT or Cake theme, don't worry, you can still use this plug-in, just add fontawesome to your blog. We provide three scheme that you can choose. 83 | 84 | ### Scheme one 85 | 86 | ```bash 87 | yarn add hexo-fontawesome 88 | ``` 89 | 90 | In `${hexo-or-theme-dir}/scripts/any.js` 91 | 92 | ```js 93 | const { dom } = require('@fortawesome/fontawesome-svg-core'); 94 | const injector = require('hexo-extend-injector2')(hexo); 95 | injector.register('style', dom.css()); 96 | ``` 97 | 98 | ### Scheme two 99 | 100 | In `${hexo-or-theme-dir}/scripts/any.js` 101 | ```js 102 | const injector = require('hexo-extend-injector2')(hexo); 103 | // add fontawesome 104 | injector.register('head-end', { 105 | value: '' 106 | }); 107 | ``` 108 | 109 | ### Scheme three 110 | 111 | Add fontawesome.css in your theme layout 112 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | 9 | This version of the GNU Lesser General Public License incorporates 10 | the terms and conditions of version 3 of the GNU General Public 11 | License, supplemented by the additional permissions listed below. 12 | 13 | 0. Additional Definitions. 14 | 15 | As used herein, "this License" refers to version 3 of the GNU Lesser 16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 17 | General Public License. 18 | 19 | "The Library" refers to a covered work governed by this License, 20 | other than an Application or a Combined Work as defined below. 21 | 22 | An "Application" is any work that makes use of an interface provided 23 | by the Library, but which is not otherwise based on the Library. 24 | Defining a subclass of a class defined by the Library is deemed a mode 25 | of using an interface provided by the Library. 26 | 27 | A "Combined Work" is a work produced by combining or linking an 28 | Application with the Library. The particular version of the Library 29 | with which the Combined Work was made is also called the "Linked 30 | Version". 31 | 32 | The "Minimal Corresponding Source" for a Combined Work means the 33 | Corresponding Source for the Combined Work, excluding any source code 34 | for portions of the Combined Work that, considered in isolation, are 35 | based on the Application, and not on the Linked Version. 36 | 37 | The "Corresponding Application Code" for a Combined Work means the 38 | object code and/or source code for the Application, including any data 39 | and utility programs needed for reproducing the Combined Work from the 40 | Application, but excluding the System Libraries of the Combined Work. 41 | 42 | 1. Exception to Section 3 of the GNU GPL. 43 | 44 | You may convey a covered work under sections 3 and 4 of this License 45 | without being bound by section 3 of the GNU GPL. 46 | 47 | 2. Conveying Modified Versions. 48 | 49 | If you modify a copy of the Library, and, in your modifications, a 50 | facility refers to a function or data to be supplied by an Application 51 | that uses the facility (other than as an argument passed when the 52 | facility is invoked), then you may convey a copy of the modified 53 | version: 54 | 55 | a) under this License, provided that you make a good faith effort to 56 | ensure that, in the event an Application does not supply the 57 | function or data, the facility still operates, and performs 58 | whatever part of its purpose remains meaningful, or 59 | 60 | b) under the GNU GPL, with none of the additional permissions of 61 | this License applicable to that copy. 62 | 63 | 3. Object Code Incorporating Material from Library Header Files. 64 | 65 | The object code form of an Application may incorporate material from 66 | a header file that is part of the Library. You may convey such object 67 | code under terms of your choice, provided that, if the incorporated 68 | material is not limited to numerical parameters, data structure 69 | layouts and accessors, or small macros, inline functions and templates 70 | (ten or fewer lines in length), you do both of the following: 71 | 72 | a) Give prominent notice with each copy of the object code that the 73 | Library is used in it and that the Library and its use are 74 | covered by this License. 75 | 76 | b) Accompany the object code with a copy of the GNU GPL and this license 77 | document. 78 | 79 | 4. Combined Works. 80 | 81 | You may convey a Combined Work under terms of your choice that, 82 | taken together, effectively do not restrict modification of the 83 | portions of the Library contained in the Combined Work and reverse 84 | engineering for debugging such modifications, if you also do each of 85 | the following: 86 | 87 | a) Give prominent notice with each copy of the Combined Work that 88 | the Library is used in it and that the Library and its use are 89 | covered by this License. 90 | 91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 92 | document. 93 | 94 | c) For a Combined Work that displays copyright notices during 95 | execution, include the copyright notice for the Library among 96 | these notices, as well as a reference directing the user to the 97 | copies of the GNU GPL and this license document. 98 | 99 | d) Do one of the following: 100 | 101 | 0) Convey the Minimal Corresponding Source under the terms of this 102 | License, and the Corresponding Application Code in a form 103 | suitable for, and under terms that permit, the user to 104 | recombine or relink the Application with a modified version of 105 | the Linked Version to produce a modified Combined Work, in the 106 | manner specified by section 6 of the GNU GPL for conveying 107 | Corresponding Source. 108 | 109 | 1) Use a suitable shared library mechanism for linking with the 110 | Library. A suitable mechanism is one that (a) uses at run time 111 | a copy of the Library already present on the user's computer 112 | system, and (b) will operate properly with a modified version 113 | of the Library that is interface-compatible with the Linked 114 | Version. 115 | 116 | e) Provide Installation Information, but only if you would otherwise 117 | be required to provide such information under section 6 of the 118 | GNU GPL, and only to the extent that such information is 119 | necessary to install and execute a modified version of the 120 | Combined Work produced by recombining or relinking the 121 | Application with a modified version of the Linked Version. (If 122 | you use option 4d0, the Installation Information must accompany 123 | the Minimal Corresponding Source and Corresponding Application 124 | Code. If you use option 4d1, you must provide the Installation 125 | Information in the manner specified by section 6 of the GNU GPL 126 | for conveying Corresponding Source.) 127 | 128 | 5. Combined Libraries. 129 | 130 | You may place library facilities that are a work based on the 131 | Library side by side in a single library together with other library 132 | facilities that are not Applications and are not covered by this 133 | License, and convey such a combined library under terms of your 134 | choice, if you do both of the following: 135 | 136 | a) Accompany the combined library with a copy of the same work based 137 | on the Library, uncombined with any other library facilities, 138 | conveyed under the terms of this License. 139 | 140 | b) Give prominent notice with the combined library that part of it 141 | is a work based on the Library, and explaining where to find the 142 | accompanying uncombined form of the same work. 143 | 144 | 6. Revised Versions of the GNU Lesser General Public License. 145 | 146 | The Free Software Foundation may publish revised and/or new versions 147 | of the GNU Lesser General Public License from time to time. Such new 148 | versions will be similar in spirit to the present version, but may 149 | differ in detail to address new problems or concerns. 150 | 151 | Each version is given a distinguishing version number. If the 152 | Library as you received it specifies that a certain numbered version 153 | of the GNU Lesser General Public License "or any later version" 154 | applies to it, you have the option of following the terms and 155 | conditions either of that published version or of any later version 156 | published by the Free Software Foundation. If the Library as you 157 | received it does not specify a version number of the GNU Lesser 158 | General Public License, you may choose any version of the GNU Lesser 159 | General Public License ever published by the Free Software Foundation. 160 | 161 | If the Library as you received it specifies that a proxy can decide 162 | whether future versions of the GNU Lesser General Public License shall 163 | apply, that proxy's public statement of acceptance of any version is 164 | permanent authorization for you to choose that version for the 165 | Library. 166 | --------------------------------------------------------------------------------