前言本教程的许多内容是"主题无关"的,这意味着即使你不是volantis
主题也能使用,但是需要一定的开发和调试能力,这部分内容会在标题和左边的目录里注明"通用 ",使用方法会写在对应部分的底部
请勿直接使用本站CDN资源,如果流量过大会开启防盗链,若需要使用请自行下载到本地
基础知识 - 小白必看如果报错 $ is not defined
,或其他有关 $
的错误,很可能是因为你没有jQuery
或者jQuery
异常,需要自行引用.注意jQuery
必须在目标代码之前,最好是第一个引用
1 <script src ="https://unpkg.com/jquery@3.6.0/dist/jquery.min.js" > </script >
某些js
需要在网页加载完毕之后再执行,否则会因为某些元素还没加载出来而报错,其中一种方法是给整段代码加上监听,这样能确保里面的代码再整个网页加载完之后再执行
1 2 3 4 let button = document .getElementById ("button" );button.onclick = (event ) => { console .log (1 ); };
1 2 3 4 5 6 window .addEventListener ('load' , ()=> { let button = document .getElementById ("button" ); button.onclick = (event ) => { console .log (1 ); }; });
第二种也是更方便的方法,是把js
代码保存在其他文件里,例如custom.js
,然后在hexo/source/_volantis/headBegin.ejs
(如果没有就自己创建)里引入自己的js
代码,注意要添加defer
1 <script defer src ="/custom.js" > </script >
defer
脚本会在网页加载完毕之后执行,且执行顺序与引入的先后顺序一致,所以jQuery
这种前置脚本要放在最前面,或者直接不加defer
自定义资源注入建议在 hexo/source/_volantis/
目录下注入自定义资源,下面是该目录下文件说明(如果没有可自行创建)
文件名 描述 first.styl 将样式以硬编码形式直接写入HTML style.styl 延迟异步加载样式 dark.styl 暗黑模式地强制覆盖样式 darkVar.styl 暗黑模式的CSS变量 headBegin.ejs <head>
标签开头注入自定义内容headEnd.ejs <head>
标签末尾注入自定义内容header.ejs 导航栏 .nav-main
末尾注入自定义内容 topMeta.ejs 侧边栏 #l_side
末尾注入自定义内容 bottomMeta.ejs topMetas
末尾注入自定义内容postEnd.ejs 页尾注入自定义内容 bodyBegin.ejs <body>
标签开头注入自定义内容bodyEnd.ejs <body>
标签末尾注入自定义内容
一般情况下,在headBegin.ejs
里引入js或css,建议把所有自己附加的js全部放到一个文件里,以减少请求次数.实际上你也可以直接在head
里写js
代码,但是非常不推荐
1 2 3 4 5 6 <script src ="https://example.com/custom.js" > </script > <script defer src ="https://example.com/custom.js" > </script > <link rel ="stylesheet" href ="https://example.com/custom.css" > <script > console .log (1 ) </script >
推荐用hexo-extend-theme 来覆盖主题本地layout
文件
首先全局安装yarn
,控制台执行以下代码,已有的可跳过
安装hexo-extend-theme
,这一步会重装hexo-server
模块,如果你以前修改过该模块则需要重新修改
1 yarn add @jiangtj/hexo-extend-theme
在hexo/_config.yml
里添加配置信息
1 2 3 theme_plus: custom_path: source/_layout
此后如果要修改主题的layout
文件,只需要把对应文件复制到source/_layout/
文件夹里就可以在不修改主题源码的情况下替换自己的代码,hexo/source/_layout/
里的文件会覆盖hexo/themes/volantis/layout/
文件夹下的同名文件,这样你也很方便地知道自己改了哪些文件
例如你想要修改hexo/themes/volantis/layout/_plugins/parallax/script.ejs
,则只需要把这个文件复制到hexo/source/_layout/_plugins/parallax/script.ejs
,此后的修改会直接覆盖源文件,并且不会破坏主题本身
暗黑模式动画(通用) 立即切换引入以下js
与css
,注意js
代码要加上defer
展开以查看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 function BackTOP ( ) { $("#btn" ).hide (); $(function ( ) { $(window ).scroll (function ( ) { if ($(window ).scrollTop () > 50 ) { $("#btn" ).fadeIn (200 ); } else { $("#btn" ).fadeOut (200 ); } }); $("#btn" ).click (function ( ) { $('body,html' ).animate ({ scrollTop : 0 }, 500 ); return false ; }); }); $(function ( ) { $("#say" ).click (function ( ) { $('body,html' ).animate ({ scrollTop : $('html, body' ).get (0 ).scrollHeight }, 500 ); return false ; }); }) } $('#readmode' ).click (function ( ) { $('body' ).toggleClass ('read-mode' ) }) function SiderMenu ( ) { $('#main-container' ).toggleClass ('open' ); $('.iconflat' ).css ('width' , '50px' ).css ('height' , '50px' ); $('.openNav' ).css ('height' , '50px' ); $('#main-container,#mo-nav,.openNav' ).toggleClass ('open' ) } function switchNightMode ( ) { $('<div class="Cuteen_DarkSky"><div class="Cuteen_DarkPlanet"></div></div>' ).appendTo ($("body" )), setTimeout ( function ( ) { (volantis.dark .mode == "dark" ) ? ($("html" ).addClass ("DarkMode" ), $('#modeicon' ).attr ("xlink:href" , "#icon-sun" )) : ($("html" ).removeClass ("DarkMode" ), $('#modeicon' ).attr ("xlink:href" , "#icon-_moon" )), setTimeout (function ( ) { $(".Cuteen_DarkSky" ).fadeOut (1e3 , function ( ) { $(this ).remove () }) }, 2e3 ) }), 50 } function checkNightMode ( ) { if ($("html" ).hasClass ("n-f" )) { $("html" ).removeClass ("day" ); $("html" ).addClass ("DarkMode" ); $('#modeicon' ).attr ("xlink:href" , "#icon-sun" ) return ; } if ($("html" ).hasClass ("d-f" )) { $("html" ).removeClass ("DarkMode" ); $("html" ).addClass ("day" ); $('#modeicon' ).attr ("xlink:href" , "#icon-_moon" ) return ; } if (volantis.dark .mode == "dark" ) { $("html" ).addClass ("DarkMode" ); $('#modeicon' ).attr ("xlink:href" , "#icon-sun" ) } else { $("html" ).removeClass ("DarkMode" ); $('#modeicon' ).attr ("xlink:href" , "#icon-_moon" ) } } BackTOP ();volantis.dark .push (switchNightMode);
展开以查看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 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 121 122 123 124 125 126 127 128 #RightDownBtn { position : fixed; left : 1.875rem ; bottom : 1.875rem ; padding : 0.3125rem 0.625rem ; background : #fff ; border-radius : 0.1875rem ; transition : 0.3s ease all; z-index : 1 ; align-items : flex-end; flex-direction : column; display : -moz-flex; display : flex; float : right; } #RightDownBtn >a ,#RightDownBtn >label { width : 1.5em ; height : 1.5em ; margin : 0.3125rem 0 ; transition : .2s cubic-bezier (.25 , .46 , .45 , .94 ); } .DarkMode #page ,.DarkMode #colophon ,.DarkMode #vcomments .vbtn ,.DarkMode .art-content #archives .al_mon_list .al_mon ,.DarkMode .art-content #archives .al_mon_list span ,.DarkMode body ,.DarkMode .art-content #archives .al_mon_list .al_mon ,.DarkMode .art-content #archives .al_mon_list span ,.DarkMode button ,.DarkMode .art .art-content #archives a ,.DarkMode textarea ,.DarkMode strong ,.DarkMode a ,.DarkMode p ,.DarkMode li ,.DarkMode .label { color : rgba (255 , 255 , 255 , .6 ); } .DarkMode #page ,.DarkMode body ,.DarkMode #colophon ,.DarkMode #main-container ,.DarkMode #page .yya ,.DarkMode #content ,.DarkMode #contentss ,.DarkMode #footer { background-color : #292a2d ; } .DarkMode strong ,.DarkMode img { filter : brightness (.7 ); } .Cuteen_DarkSky ,.Cuteen_DarkSky :before { content : "" ; position : fixed; left : 0 ; right : 0 ; top : 0 ; bottom : 0 ; z-index : 88888888 } .Cuteen_DarkSky { background : linear-gradient (#feb8b0 , #fef9db ) } .Cuteen_DarkSky :before { transition : 2s ease all; opacity : 0 ; background : linear-gradient (#4c3f6d , #6c62bb , #93b1ed ) } .DarkMode .Cuteen_DarkSky :before { opacity : 1 } .Cuteen_DarkPlanet { z-index : 99999999 ; position : fixed; left : -50% ; top : -50% ; width : 200% ; height : 200% ; -webkit-animation : CuteenPlanetMove 2s cubic-bezier (.7 , 0 , 0 , 1 ); animation : CuteenPlanetMove 2s cubic-bezier (.7 , 0 , 0 , 1 ); transform-origin : center bottom } @-webkit-keyframes CuteenPlanetMove { 0% { transform : rotate (0 ) } to { transform : rotate (360deg ) } } @keyframes CuteenPlanetMove { 0% { transform : rotate (0 ) } to { transform : rotate (360deg ) } } .Cuteen_DarkPlanet :after { position : absolute; left : 35% ; top : 40% ; width : 9.375rem ; height : 9.375rem ; border-radius : 50% ; content : "" ; background : linear-gradient (#fefefe , #fffbe8 ) }
非``volantis``的使用方法 首先按上文方法引入css
,如果你的主题开发者提供了暗黑模式接口,则以defer
方式引入并修改以下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 function BackTOP ( ) { $("#btn" ).hide (); $(function ( ) { $(window ).scroll (function ( ) { if ($(window ).scrollTop () > 50 ) { $("#btn" ).fadeIn (200 ); } else { $("#btn" ).fadeOut (200 ); } }); $("#btn" ).click (function ( ) { $('body,html' ).animate ({ scrollTop : 0 }, 500 ); return false ; }); }); $(function ( ) { $("#say" ).click (function ( ) { $('body,html' ).animate ({ scrollTop : $('html, body' ).get (0 ).scrollHeight }, 500 ); return false ; }); }) } $('#readmode' ).click (function ( ) { $('body' ).toggleClass ('read-mode' ) }) function SiderMenu ( ) { $('#main-container' ).toggleClass ('open' ); $('.iconflat' ).css ('width' , '50px' ).css ('height' , '50px' ); $('.openNav' ).css ('height' , '50px' ); $('#main-container,#mo-nav,.openNav' ).toggleClass ('open' ) } function switchNightMode ( ) { $('<div class="Cuteen_DarkSky"><div class="Cuteen_DarkPlanet"></div></div>' ).appendTo ($("body" )), setTimeout ( function ( ) { (YourTheme .dark .mode == "dark" ) ? ($("html" ).addClass ("DarkMode" ), $('#modeicon' ).attr ("xlink:href" , "#icon-sun" )) : ($("html" ).removeClass ("DarkMode" ), $('#modeicon' ).attr ("xlink:href" , "#icon-_moon" )), setTimeout (function ( ) { $(".Cuteen_DarkSky" ).fadeOut (1e3 , function ( ) { $(this ).remove () }) }, 2e3 ) }), 50 } function checkNightMode ( ) { if ($("html" ).hasClass ("n-f" )) { $("html" ).removeClass ("day" ); $("html" ).addClass ("DarkMode" ); $('#modeicon' ).attr ("xlink:href" , "#icon-sun" ) return ; } if ($("html" ).hasClass ("d-f" )) { $("html" ).removeClass ("DarkMode" ); $("html" ).addClass ("day" ); $('#modeicon' ).attr ("xlink:href" , "#icon-_moon" ) return ; } if (volantis.dark .mode == "dark" ) { $("html" ).addClass ("DarkMode" ); $('#modeicon' ).attr ("xlink:href" , "#icon-sun" ) } else { $("html" ).removeClass ("DarkMode" ); $('#modeicon' ).attr ("xlink:href" , "#icon-_moon" ) } } BackTOP ();YourTheme .dark .callbacks .push (switchNightMode);
首页动态诗词(通用)在volantis的配置文件里修改 subtitle 为"<div id="binft"></div>
"
1 2 3 4 5 ############################### Cover ############################### > start cover: ... subtitle: <div id ="binft" > </div > ...
以defer
方式引入以下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 var binft = function (r ) { var isTransparent = true ; function getRandomColor ( ) { if (isTransparent){ isTransparent = false ; return "rgba(255,255,255,0)" }else { isTransparent = true ; return "rgba(255,255,255,1)" } } function n (r ) { for (var n = document .createDocumentFragment (), i = 0 ; r > i; i++) { var oneword = document .createElement ("span" ); oneword.textContent = "_" ; oneword.style .color = getRandomColor (); n.appendChild (oneword); } return n } function i ( ) { var t = wordList[c.skillI ]; c.step ? c.step -- : (c.step = refreshDelayTime, c.prefixP < l.length ? (c.prefixP >= 0 && (c.text += l[c.prefixP ]), c.prefixP ++) : "forward" === c.direction ? c.skillP < t.length ? (c.text += t[c.skillP ], c.skillP ++) : c.delay ? c.delay -- : (c.direction = "backward" , c.delay = showTotalWordDelayTime) : c.skillP > 0 ? (c.text = c.text .slice (0 , -1 ), c.skillP --) : (c.skillI = (c.skillI + 1 ) % wordList.length , c.direction = "forward" )), r.textContent = c.text , r.appendChild (n (c.prefixP < l.length ? Math .min (maxLength, maxLength + c.prefixP ) : Math .min (maxLength, t.length - c.skillP ))), setTimeout (i, d) } var l = "" , wordList = [ "有花堪折直需折,莫待无花空折枝." , "闲居少邻并,草径入荒园.鸟宿池边树,僧敲月下门." , "侯门一入深如海,从此萧郎是路人." , "才见岭头云似盖,已惊岩下雪如尘." , "人间万事消磨尽,只有清香似旧时." , "日暮酒醒人已远,满天风雨下西楼." , "落灯花,棋未收,叹新丰逆旅淹留." , "软风吹过窗纱,心期便隔天涯." , "迷惑失故路,薄暮无宿栖." , "不见白头相携老,只许与君共天明." , "晓迎秋露一枝新,不占园中最上春." , "荷尽已无擎雨盖,菊残犹有傲霜枝." , "春未绿,鬓先丝.人间别久不成悲." , "江东子弟多才俊,卷土重来未可知." , "莫听穿林打叶声,何妨吟啸且徐行." , "在天愿作比翼鸟,在地愿为连理枝." , ].map (function (r ) { return r + "" }), showTotalWordDelayTime = 2 , refreshDelayTime = 1 , maxLength = 1 , d = 75 , c = { text : "" , prefixP : -maxLength, skillI : 0 , skillP : 0 , direction : "forward" , delay : showTotalWordDelayTime, step : refreshDelayTime }; i () }; binft (document .getElementById ('binft' ));
非volantis的使用方法 在需要显示动态诗词的位置(如副标题),将文本替换为"<div id="binft"></div>
",然后执行上面的js
代码即可
你的主题配置文件里应该会提供副标题的修改
看板娘随音乐启停说话(通用)如果你仅仅想要获取Aplayer对象并自己实现监听,使用以下代码可以获取第一个metingjs
里的Aplayer对象.如果你想要实现看板娘随音乐启停说话,请跳过这句话.
1 let aplayer = document .querySelectorAll ("meting-js" )[0 ].aplayer ;
新版看板娘已失效,这里直接给出已经修改好的旧版看板娘源文件
引用看板娘js,此处改为你自己的地址,注意 autoload.js 必须在页面加载完毕之后再执行,否则容易报错
1 <script defer src ="/live2d-widget/autoload.js" > </script >
打开 autoload.js,将 live2d_path 改为你的 widget 文件夹的地址
1 2 3 const live2d_path = "/live2d-widget/" ;
下面的 cdnPath 同理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 if (screen.width >= 768 ) { Promise .all ([ loadExternalResource (live2d_path + "waifu.css" , "css" ), loadExternalResource (live2d_path + "live2d.min.js" , "js" ), loadExternalResource (live2d_path + "waifu-tips.js" , "js" ) ]).then (() => { initWidget ({ waifuPath : live2d_path + "waifu-tips.json" , cdnPath : "/live2d_api/" }); }); }
我已经在 waifu-tips.js 里定义好了 say()
函数,直接在控制台执行 say("Hello")
即可看到看板娘说话
立即尝试如果想要实现随音乐启停说话,只需在head
里引入或执行下面的js,注意修改最底下的3个数组,换成你自己想要的内容
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 window .addEventListener ('load' , function ( ) { let interval = setInterval (()=> { let aplayer = document .querySelectorAll ("meting-js" )[0 ].aplayer ; if (!aplayer){ return ; } aplayer.on ("play" , ()=> { music_index = aplayer.list .index ; music_name = aplayer.list .audios [music_index].title ; music_artist = aplayer.list .audios [music_index].artist ; if (say){ say (randomSeek (message_play)); } }); aplayer.on ("pause" , ()=> { if (say){ say (randomSeek (message_pause)); } }); aplayer.on ("seeked" , ()=> { if (say){ say (randomSeek (message_seeked)); } }); clearInterval (interval) function randomSeek (list ){ return list[Math .floor (Math .random () * list.length )] .replaceAll ("{name}" , `<span>${music_name || "未知歌曲" } </span>` ) .replaceAll ("{artist}" , `<span>${music_artist || "未知作者" } </span>` ); } }, 300 ) let music_index; let music_name; let music_artist; const message_play = [ '开始播放{name}' ,]; const message_pause = [ '{name}已暂停' ,]; const message_seeked = [ '快进' ,]; });
非volantis的使用方法 "Hello World"特效(通用)修改主题配置文件里的标题
1 2 3 4 5 cover: ... title: '<font><span>Hello</span> <span>World</span></font>' ...
引入以下css样式,推荐添加到 hexo/source/_volantis/style.styl 里
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 .top .title span { transition : 0.5s ; } .top .title :hover span :nth-child (1 ){ margin-right : 10px ; } .top .title :hover span :nth-child (2 ){ margin-left : 10px ; } .top .title :hover span { color : #fff ; text-shadow : 0 0 10px #fff , 0 0 20px #fff , 0 0 40px #fff , 0 0 80px #fff , 0 0 120px #fff , 0 0 160px #fff ; }
非volantis的使用方法 将你的标题修改为
1 <div class ="title_example" > <font > <span > Hello</span > <span > World</span > </font > </div >
添加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 .title_example { font-size : 3em ; color : white; margin : auto; display : table; align-items : center; } .title_example span { transition : 0.5s ; } .title_example :hover span :nth-child (1 ){ margin-right : 10px ; } .title_example :hover span :nth-child (2 ){ margin-left : 10px ; } .title_example :hover span { color : #fff ; text-shadow : 0 0 10px #fff , 0 0 20px #fff , 0 0 40px #fff , 0 0 80px #fff , 0 0 120px #fff , 0 0 160px #fff ; }
此外,如果你的主题没有将标题居中,那么你还需要自己实现居中
表格居中Hexo默认将表格居左
添加 css 样式后居中
在任意位置引入以下css即可
1 2 3 4 table :not (figure table ){ display : table !important ; margin : auto; }
需要注意的是,你的博客里可能有其他元素(例如Volantis的代码块)也是由表格组成,盲目使用css可能会导致显示异常,需要在not()
中排除,否则就会像下面的代码块一样
未排除figure table时的异常 代码块
1 2 3 4 COPY int sum=0 ;for (int i=0 ;i<100 ;i++){ sum += i; }
KaTeX公式渲染(通用)KeTeX支持markdown内公式渲染,是常用的网页公式渲染器,以下展示KaTeX渲染效果,示例来自于上一篇文章 的例题一
设函数f ( x , y ) f(x,y) f ( x , y ) 的全微分为d f ( x , y ) = ( 2 a x + b y ) d x + ( 2 b y + a x ) d y df(x,y)=(2ax+by)dx+(2by+ax)dy d f ( x , y ) = ( 2 a x + b y ) d x + ( 2 b y + a x ) d y ,(a a a ,b b b 为常数),且f ( 0 , 0 ) = − 3 , f x ′ ( 1 , 1 ) = 3 f(0,0)=-3,f_{x}^{'}(1,1)=3 f ( 0 , 0 ) = − 3 , f x ′ ( 1 , 1 ) = 3 ,求f ( x , y ) f(x,y) f ( x , y )
本题给的是全微分,但是可以看成两个偏微分,并且较为基础,所以放在第一题
∂ f ∂ x = 2 a x + b y , ∂ f ∂ y = 2 b y + a x \frac{∂f}{∂x}=2ax+by,\frac{∂f}{∂y}=2by+ax ∂ x ∂ f = 2 a x + b y , ∂ y ∂ f = 2 b y + a x
直接对两个偏微分求不定积分,可以得到原函数.注意对x积分时,将y看作常数,因此最后的+ C +C + C 实际上应该写作+ g ( y ) +g(y) + g ( y )
f ( x , y ) = a x 2 + b x y + g ( y ) = b y 2 + a x y + h ( x ) f(x,y)=ax^{2}+bxy+g(y)=by^{2}+axy+h(x) f ( x , y ) = a x 2 + b x y + g ( y ) = b y 2 + a x y + h ( x )
显然两者是同一个函数,因此对应的项的系数也相同,即a = b a=b a = b ,对x求偏导,得到f x ′ ( 1 , 1 ) = 2 a + b = 3 f_{x}^{'}(1,1)=2a+b=3 f x ′ ( 1 , 1 ) = 2 a + b = 3 ,故f ( x , y ) = x 2 + x y + y 2 − 3 f(x,y)=x^2+xy+y^2-3 f ( x , y ) = x 2 + x y + y 2 − 3
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 {% noteblock quote %} 设函数$f(x,y)$的全微分为$df(x,y)=(2ax+by)dx+(2by+ax)dy$,($a$,$b$为常数),且$f(0,0)=-3,f_{x}^{'}(1,1)=3$,求$f(x,y)$ {% endnoteblock %} 本题给的是全微分,但是可以看成两个偏微分,并且较为基础,所以放在第一题 $$ \frac{∂f}{∂x}=2ax+by,\frac{∂f}{∂y}=2by+ax $$ 直接对两个偏微分求不定积分,可以得到原函数.注意对x积分时,将y看作常数,因此最后的$+C$实际上应该写作$+g(y)$ $$ f(x,y)=ax^{2}+bxy+g(y)=by^{2}+axy+h(x) $$ 显然两者是同一个函数,因此对应的项的系数也相同,即$a=b$,对x求偏导,得到$f_ {x}^{'}(1,1)=2a+b=3$,故$f(x,y)=x^2+xy+y^2-3$
首先切换渲染器,在hexo目录下控制台执行以下代码(参考自在hexo上使用KaTeX - MicDZ’s blog )
1 2 npm uninstall hexo-renderer-marked --save npm install hexo-renderer-markdown-it-plus --save
引入以下js与css
1 2 3 <link rel ="stylesheet" href ="https://unpkg.com/katex@0.16.4/dist/katex.min.css" > <script defer src ="https://unpkg.com/katex@0.16.4/dist/katex.min.js" > </script > <script defer src ="https://unpkg.com/katex@0.16.4/dist/contrib/auto-render.min.js" > <script >
可选功能 复制公式时替换为KaTeX源码
1 <script src ="https://unpkg.com/katex@0.16.4/dist/contrib/copy-tex.min.js" > </script >
公式显示不下时添加滑动条
1 2 3 4 .katex-block { overflow-x : auto; overflow-y : auto; }
F → = G ( ∭ Ω ( x − x 0 ) f ( x , y , z ) r 3 d v , ∭ Ω ( y − y 0 ) f ( x , y , z ) r 3 d v , ∭ Ω ( z − z 0 ) f ( x , y , z ) r 3 d v ) \Large\mathop{F}\limits ^{\rightarrow}=G\left(\iiint\limits_Ω\frac{(x-x_0)f(x,y,z)}{r^3}dv,\iiint\limits_Ω\frac{(y-y_0)f(x,y,z)}{r^3}dv,\iiint\limits_Ω\frac{(z-z_0)f(x,y,z)}{r^3}dv\right) F → = G ⎝ ⎜ ⎜ ⎜ ⎜ ⎛ Ω ∭ r 3 ( x − x 0 ) f ( x , y , z ) d v , Ω ∭ r 3 ( y − y 0 ) f ( x , y , z ) d v , Ω ∭ r 3 ( z − z 0 ) f ( x , y , z ) d v ⎠ ⎟ ⎟ ⎟ ⎟ ⎞
无奖竞猜:上面的公式表示什么物理量?
现在你已经可以在博客与twikoo
评论系统中直接插入公式,详细说明请查看KaTeX文档
在下面的输入框输入KaTeX表达式,来快速预览
∬ D ( ∂ Q ∂ x − ∂ P ∂ y ) d x d y = ∮ L P d x + Q d y \Large\iint\limits_D(\frac{\partial Q}{\partial x}-\frac{\partial P}{\partial y})dxdy=\oint_LPdx+Qdy D ∬ ( ∂ x ∂ Q − ∂ y ∂ P ) d x d y = ∮ L P d x + Q d y
非volantis的使用方法 动态标题(通用)在用户离开与返回本标签页时修改页面标题,引入以下js
1 2 3 4 5 6 7 8 9 10 11 12 13 var OriginTitle = document .title ;document .addEventListener ('visibilitychange' , function ( ) { if (document .hidden ) { document .title = '╭(°A°`)╮ 你去哪了? 快回来!!!' ; }else { document .title = '(ฅ>ω<*ฅ) 你终于回来了 ~' ; setTimeout (function ( ) { if (!document .hidden ){ document .title = OriginTitle ; } }, 2000 ); } });
非volantis的使用方法 段落缩进(本站未使用)在_volantis/style.styl
里添加以下css
1 2 3 4 5 6 7 8 9 10 11 12 13 // 标题居左,开启后会使主页居左 .post .post-v3 .white-box { text-align : left text-indent: 2em } // 文章段落开头空两格 .md { text-align : left text-indent: 2em }
网页压缩(通用)在网址前面加上view-source:
即可看到本页面压缩效果
在hexo目录下打开控制台,输入
1 2 3 npm install html-minifier npm install uglify-js npm install clean-css
在当前目录下创建 minify.js
,输入以下代码,其中config
可自行修改
展开以显示代码 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 const minify = require ("html-minifier" ).minify ;const uglify = require ("uglify-js" ).minify ;const cleancss = require ("clean-css" );const fs = require ("fs" );const path = require ("path" );const config = { root : "public" , encoding : "utf-8" , Html : true , Js : true , Css : true , JsInHtml : true , CssInHtml : true , showSuccess : true , showError : true , showDetail : false } let total = 0 ;let success = 0 ;LoadDir (config.root );console .log (`共压缩${total} 个文件,其中${success} 个压缩成功` );function LoadDir (dir ){ fs.readdirSync (dir).forEach ((filename ) => { const filePath = path.join (dir, filename); if (fs.statSync (filePath).isFile ()){ if (filePath.endsWith (".html" ) && config.Html ){ total++; CompressHTML (filePath); }else if (filePath.endsWith (".js" ) && config.Js ){ total++; CompressJS (filePath); }else if (filePath.endsWith (".css" ) && config.Css ){ total++; CompressCSS (filePath); } }else { LoadDir (filePath); } }); } function CompressHTML (file ){ try { const data = fs.readFileSync (file, config.encoding ); const result = minify (data, { removeComments : true , collapseWhitespace : true , minifyJS : config.JsInHtml , minifyCSS : config.CssInHtml }); if (data !== result){ fs.writeFileSync (file,result, {encoding : config.encoding }); } if (config.showSuccess ){ console .log (`压缩率:${(result.length * 100 / data.length).toFixed(2 )} %,文件:${file} ` ); } success++; }catch (e){ if (config.showError ){ console .error (`文件(${file} )压缩失败` ); } if (config.showDetail ){ console .error (e); } } } function CompressJS (file ){ try { const data = fs.readFileSync (file, config.encoding ); const result = uglify (data).code ; if (data !== result){ fs.writeFileSync (file,result, {encoding : config.encoding }); } if (config.showSuccess ){ console .log (`压缩率:${(result.length * 100 / data.length).toFixed(2 )} %,文件:${file} ` ); } success++; }catch (e){ if (config.showError ){ console .error (`文件(${file} )压缩失败` ); } if (config.showDetail ){ console .error (e); } } } function CompressCSS (file ){ try { const data = fs.readFileSync (file, config.encoding ); const result = new cleancss ({returnPromise : false }).minify (data).styles ; if (data !== result){ fs.writeFileSync (file,result, {encoding : config.encoding }); } if (config.showSuccess ){ console .log (`压缩率:${(result.length * 100 / data.length).toFixed(2 )} %,文件:${file} ` ); } success++; }catch (e){ if (config.showError ){ console .error (`文件(${file} )压缩失败` ); } if (config.showDetail ){ console .error (e); } } }
先使用 hexo g
生成静态文件,然后控制台输入以下指令压缩网页
压缩范围包括html,js,css
,以及html
内嵌的css,js
,如果出现语法错误会压缩失败
非volantis的使用方法 今日诗词改成指定的文字修改或覆盖hexo/themes/volantis/layout/_widget/blogger.ejs
先搜索找到item.jinrishici
的位置,在下方添加item.customword
,如下所示
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 <% if (item.title || item.subtitle || item.jinrishici ) { %> <div class ='text' > <% if (item.title){ %> <h2 > <%- item.title %></h2 > <% } %> <% if (item.subtitle){ %> <%- markdown(item.subtitle) %> <% } %> <% if (item.jinrishici){ %> <p > <span id ="jinrishici-sentence" > <%- item.jinrishici != true ? item.jinrishici : config.title %></span > </p > <script src ="https://sdk.jinrishici.com/v2/browser/jinrishici.js" charset ="utf-8" > </script > <% } %> <% if (item.customword){ %> <p > <%- item.customword %></p > <% } %> </div > <% } %>
修改hexo/_config.volantis.yml
,找到sidebar
下的blogger
,添加customword
1 2 3 4 5 6 7 8 9 10 11 12 sidebar: ... widget_library: blogger: ... jinrishici: false customword: 道可道,非常道;名可名,非常名. ...
透明卡片与背景亮度在hexo/source/_volantis/style.styl
里添加以下css
1 2 3 4 5 6 7 8 9 10 11 12 13 #l_main .post ,#l_side .widget opacity : 0.8 #post .post ,#docs .post opacity : 1 .parallax-mirror filter : brightness (0.5 )
Aplayer采用本地音源(通用)metingjs
的官方api不稳定,因此建议改用本地音源来确保稳定播放.该方法在不影响Volantis原有功能的情况下,通过修改metingjs
来实现加载本地音乐的功能
修改或覆盖hexo/themes/volantis/layout/_plugins/aplayer/layout.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 <% let aplayer = theme.plugins .aplayer ;%> <% if (theme.plugins .aplayer .enable == true ) { %> <% if (aplayer.localMusic .enable == true ) { <% let audio = aplayer.localMusic .audio ; let template = aplayer.localMusic .template ; audio.forEach ((item ) => { let name = item.name ; if (!item.artist ) item.artist = template.artist .replaceAll ("{name}" , name); if (!item.title ) item.title = template.title .replaceAll ("{name}" , name); if (!item.url ) item.url = template.root + template.url .replaceAll ("{name}" , name); if (!item.lrc ) item.lrc = template.root + template.lrc .replaceAll ("{name}" , name); if (!item.cover ) item.cover = template.root + template.cover .replaceAll ("{name}" , name); }); %> <script > const MetingPlusAudio = <%- JSON .stringify (audio) %>; </script > <meting-js theme ='<%- aplayer.theme %>' autoplay ='<%- aplayer.autoplay %>' volume ='<%- aplayer.volume %>' loop ='<%- aplayer.loop %>' order ='<%- aplayer.order %>' fixed ='<%- aplayer.fixed %>' list-max-height ='<%- aplayer.list_max_height %>' server ='<%- aplayer.server %>' type ='<%- aplayer.type %>' id ='<%- aplayer.id %>' list-folded ='<%- aplayer.list_folded %>' audio ='MetingPlusAudio' > </meting-js > <% } else { %> <% if (post && post.music ) { %> <meting-js mini ='true' volume ='<%- post.music.volume || aplayer.volume %>' loop ='<%- post.music.loop || aplayer.loop %>' order ='<%- post.music.order || aplayer.order %>' server ='<%- post.music.server || aplayer.server %>' type ='<%- post.music.type || aplayer.type %>' id ='<%- post.music.id || aplayer.id %>' > </meting-js > <% } else { %> <meting-js theme ='<%- aplayer.theme %>' autoplay ='<%- aplayer.autoplay %>' volume ='<%- aplayer.volume %>' loop ='<%- aplayer.loop %>' order ='<%- aplayer.order %>' fixed ='<%- aplayer.fixed %>' list-max-height ='<%- aplayer.list_max_height %>' server ='<%- aplayer.server %>' type ='<%- aplayer.type %>' id ='<%- aplayer.id %>' list-folded ='<%- aplayer.list_folded %>' > </meting-js > <% } %> <% } %> <% } %>
创建meting-plus.js
文件,输入以下代码.该代码已发表在Gitee
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 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 class MetingJSElement extends HTMLElement { connectedCallback ( ) { if (window .APlayer && window .fetch ) { this ._init () this ._parse () } } disconnectedCallback ( ) { if (!this .lock ) { this .aplayer .destroy () } } _camelize (str ) { return str .replace (/^[_.\- ]+/ , '' ) .toLowerCase () .replace (/[_.\- ]+(\w|$)/g , (m, p1 ) => p1.toUpperCase ()) } _init ( ) { let config = {} for (let i = 0 ; i < this .attributes .length ; i += 1 ) { config[this ._camelize (this .attributes [i].name )] = this .attributes [i].value } let keys = [ 'server' , 'type' , 'id' , 'api' , 'auth' , 'auto' , 'lock' , 'name' , 'title' , 'artist' , 'author' , 'url' , 'cover' , 'pic' , 'lyric' , 'lrc' , 'audio' , ] this .meta = {} for (let key of keys) { this .meta [key] = config[key] delete config[key] } this .config = config this .api = this .meta .api || window .meting_api || 'https://api.i-meto.com/meting/api?server=:server&type=:type&id=:id&r=:r' if (this .meta .auto ) this ._parse_link () } _parse_link ( ) { let rules = [ ['music.163.com.*song.*id=(\\d+)' , 'netease' , 'song' ], ['music.163.com.*album.*id=(\\d+)' , 'netease' , 'album' ], ['music.163.com.*artist.*id=(\\d+)' , 'netease' , 'artist' ], ['music.163.com.*playlist.*id=(\\d+)' , 'netease' , 'playlist' ], ['music.163.com.*discover/toplist.*id=(\\d+)' , 'netease' , 'playlist' ], ['y.qq.com.*song/(\\w+).html' , 'tencent' , 'song' ], ['y.qq.com.*album/(\\w+).html' , 'tencent' , 'album' ], ['y.qq.com.*singer/(\\w+).html' , 'tencent' , 'artist' ], ['y.qq.com.*playsquare/(\\w+).html' , 'tencent' , 'playlist' ], ['y.qq.com.*playlist/(\\w+).html' , 'tencent' , 'playlist' ], ['xiami.com.*song/(\\w+)' , 'xiami' , 'song' ], ['xiami.com.*album/(\\w+)' , 'xiami' , 'album' ], ['xiami.com.*artist/(\\w+)' , 'xiami' , 'artist' ], ['xiami.com.*collect/(\\w+)' , 'xiami' , 'playlist' ], ] for (let rule of rules) { let patt = new RegExp (rule[0 ]) let res = patt.exec (this .meta .auto ) if (res !== null ) { this .meta .server = rule[1 ] this .meta .type = rule[2 ] this .meta .id = res[1 ] return } } } _parse ( ) { if (this .meta .audio ) { let audioList if (this .meta .audio .trim ().startsWith ("%5B" )){ audioList = JSON .parse (decodeURI (this .meta .audio )) }else { audioList = eval (this .meta .audio ) } this ._loadPlayer (audioList) return } if (this .meta .url ) { let result = { name : this .meta .name || this .meta .title || 'Audio name' , artist : this .meta .artist || this .meta .author || 'Audio artist' , url : this .meta .url , cover : this .meta .cover || this .meta .pic , lrc : this .meta .lrc || this .meta .lyric || '' , type : this .meta .type || 'auto' , } if (!result.lrc ) { this .meta .lrcType = 0 } if (this .innerText ) { result.lrc = this .innerText this .meta .lrcType = 2 } this ._loadPlayer ([result]) return } let url = this .api .replace (':server' , this .meta .server ) .replace (':type' , this .meta .type ) .replace (':id' , this .meta .id ) .replace (':auth' , this .meta .auth ) .replace (':r' , Math .random ()) fetch (url) .then (res => res.json ()) .then (result => this ._loadPlayer (result)) } _loadPlayer (data ) { let defaultOption = { audio : data, mutex : true , lrcType : this .meta .lrcType || 3 , storageName : 'metingjs' } if (!data.length ) return let options = { ...defaultOption, ...this .config , } for (let optkey in options) { if (options[optkey] === 'true' || options[optkey] === 'false' ) { options[optkey] = (options[optkey] === 'true' ) } } let div = document .createElement ('div' ) options.container = div this .appendChild (div) this .aplayer = new APlayer (options) } } if (window .customElements && !window .customElements .get ('meting-js' )) { window .MetingJSElement = MetingJSElement window .customElements .define ('meting-js' , MetingJSElement ) }
修改配置文件hexo/_config.volantis.yml
,将js.meting
修改成你刚刚创建的meting-plus.js
的路径,并添加localMusic
,如下所示
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 plugins: ... aplayer: enable: true js: aplayer: https://unpkg.com/aplayer@1.10/dist/APlayer.min.js meting: /meting-plus.js css: https://unpkg.com/aplayer@1.10/dist/APlayer.min.css ... localMusic: enable: true template: root: '/music/' title: '{name}' artist: '{name}' url: '{name}.mp3' lrc: '{name}.lrc' cover: '{name}.jpg' audio: - name: 'Flower Dance' title: 'Flower Dance' artist: 'Oturans - DJ Okawari' url: '/music/Flower Dance.mp3' lrc: '/music/Flower Dance.lrc' cover: '/music/Flower Dance.jpg' - name: '夜的钢琴曲' artist: 'K. Williams' ...
localMusic.enable
决定了是否启用本地音源,localMusic.audio
是本地音乐列表,含义如下
参数 含义 name 歌曲名,会显示在Aplayer中 title 标题,Volantis自带的播放提示会以此为准,基本上设置成跟name一样就好 artist 作者 url 音乐路径 lrc 歌词文件路径 cover 封面图片路径
考虑到这里可能会出现大量重复操作,故提供了localMusic.template
模板功能,在填写localMusic.audio
时,你至少要填写name
属性,缺失的属性会从localMusic.template
里获取,其中字符串里的{name}
会被替换为localMusic.audio.name
,localMusic.template.root
是前缀路径,会被添加在url,lrc,cover
前面
例如你设置了
1 2 3 4 5 6 7 8 9 10 11 12 localMusic: enable: true template: root: 'https://example.com/music/' title: '{name}' artist: 'This is {name}' url: '{name}.mp3' lrc: 'all.lrc' cover: 'https://example.com/music/{name}.jpg' audio: - name: 'Flower Dance' title: '花之舞'
则使用模板替换后的实际audio
是
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 audio: - name: 'Flower Dance' title: '花之舞' artist: 'This is Flower Dance' url: 'https://example.com/music/Flower Dance.mp3' lrc: 'https://example.com/music/all.lrc' cover: 'https://example.com/music/https://example.com/music/Flower Dance.jpg'
需要自定义功能的可以自行修改hexo/themes/volantis/layout/_plugins/aplayer/layout.ejs
非volantis的使用方法 如果你的主题也是使用metingjs
来生成Aplayer,那么你需要先把metingjs
改成上面的meting-plus
,然后根据你的配置文件规则,修改上面的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 <% if (aplayer.localMusic .enable == true ) { <% let audio = aplayer.localMusic .audio ; let template = aplayer.localMusic .template ; audio.forEach ((item ) => { let name = item.name ; if (!item.artist ) item.artist = template.artist .replaceAll ("{name}" , name); if (!item.title ) item.title = template.title .replaceAll ("{name}" , name); if (!item.url ) item.url = template.root + template.url .replaceAll ("{name}" , name); if (!item.lrc ) item.lrc = template.root + template.lrc .replaceAll ("{name}" , name); if (!item.cover ) item.cover = template.root + template.cover .replaceAll ("{name}" , name); }); %> <script > const MetingPlusAudio = <%- JSON .stringify (audio) %>; </script > <meting-js theme ='<%- aplayer.theme %>' autoplay ='<%- aplayer.autoplay %>' volume ='<%- aplayer.volume %>' loop ='<%- aplayer.loop %>' order ='<%- aplayer.order %>' fixed ='<%- aplayer.fixed %>' list-max-height ='<%- aplayer.list_max_height %>' server ='<%- aplayer.server %>' type ='<%- aplayer.type %>' id ='<%- aplayer.id %>' list-folded ='<%- aplayer.list_folded %>' audio ='MetingPlusAudio' > </meting-js > <% }%>
多背景图与横竖屏优化如果你的背景图按照某一规律命名(例如数字.jpg
),且数量很多,则可以修改主题代码,每次随机生成.但如果你是上传到图床,且命名都是随机的,那么就老老实实去配置文件hexo\_config.volantis.yml
里写
假设你把背景图放在https://cdn.example.com/img/
目录下,且图片命名是1.jpg
到100.jpg
,那么你可以按照以下示例修改代码
修改或覆盖hexo/themes/volantis/layout/_plugins/parallax/script.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 <script > const img_config = { min : 1 , max : 100 , root : "https://cdn.example.com/img/" } let IntervalParallax = null ;function parallax ( ) { <% if (theme.plugins .parallax .position == "fixed" ) { %> let ParallaxWindow = document .querySelector ("html" ); <% } else { %> let ParallaxWindow = document .querySelector ("#parallax-window" ); <% } %> Parallax .window = ParallaxWindow ; Parallax .options .fade = <%- theme.plugins .parallax .fade %>; Parallax .cache = 1 ; next_parallax (); Parallax .init (); IntervalParallax = setInterval (function ( ) { if (!document .hidden ){ next_parallax (); } }, '<%- theme.plugins.parallax.duration %>' ); } function next_parallax ( ) { if (typeof Parallax == "undefined" ) { return } <% if (theme.plugins .parallax .position != "fixed" ) { %> if (!document .querySelector ("#full" ) && !document .querySelector ("#half" )) { return } <% } %> let index = Math .floor (Math .random () * (img_config.max - img_config.min + 1 ) + img_config.min ); let img = `${img_config.root} ${index} .jpg` ; Parallax .options .src = img; Parallax .start (); } var runningOnBrowser = typeof window !== "undefined" ;var isBot = runningOnBrowser && !("onscroll" in window ) || typeof navigator !== "undefined" && /(gle|ing|ro|msn)bot|crawl|spider|yand|duckgo/i .test (navigator.userAgent );if (!isBot) { volantis.js ('<%- theme.cdn.map.js.parallax %>' ).then (() => { parallax () }) volantis.pjax .send (() => { clearInterval (IntervalParallax ) }, "clearIntervalParallax" ); volantis.pjax .push (parallax); } </script >
如果希望根据横竖屏加载不同的壁纸,则修改上面的next_parallax()
函数,如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 function next_parallax ( ) { if (typeof Parallax == "undefined" ) { return } <% if (theme.plugins .parallax .position != "fixed" ) { %> if (!document .querySelector ("#full" ) && !document .querySelector ("#half" )) { return } <% } %> let width = window .innerWidth || document .body .clientWidth ; let height = window .innerHeight || document .body .clientHeight ; let client = height / width > 1.3 ? "mobile" : "pc" ; let img = `https://api.dearxuan.com/image?type=${client} ` Parallax .options .src = img; Parallax .start (); }
或者你也可以使用下面的代码,来判断浏览器类型,从而返回不同的图片,但是要注意手机不一定全是竖屏,电脑也不一定全是横屏,推荐用上面的长宽比来判断
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 var client;var p = navigator.userAgent ;if (p.toLowerCase ().includes ("qq" )){ client = "qq" }else if (p.toLowerCase ().includes ("micromessenger" )){ client = "weixin" }else if (p.includes ("Win" )){ client = "windows" }else if (p.includes ("Mac" )){ client = "mac" }else if (p.includes ("X11" )){ client = "linux" }else if (p.includes ("Android" )){ client = "android" }else if (p.toLowerCase ().includes ("ipad" )){ client = "ipad" }else if (p.includes ("iPhone" )){ client = "iphone" }else { client = "other" }