diff --git a/Plugin.php b/Plugin.php index 738bd66..e48f934 100644 --- a/Plugin.php +++ b/Plugin.php @@ -4,7 +4,7 @@ * * @package Pio * @author Dreamer-Paul - * @version 1.3 + * @version 2.0 * @link https://paugram.com */ @@ -39,15 +39,15 @@ class Pio_Plugin implements Typecho_Plugin_Interface{ echo ""; } - paul_update("Pio", "1.3"); + paul_update("Pio", "2.0"); // 读取模型文件夹 $models = array(); $load = glob("../usr/plugins/Pio/models/*"); - foreach($load as $key => &$value){ - $aaa = substr($value, 26); - $models[$aaa] = ucfirst($aaa); + foreach($load as $key => $value){ + $single = substr($value, 26); + $models[$single] = ucfirst($single); }; // 自定义模型选择 @@ -73,6 +73,33 @@ class Pio_Plugin implements Typecho_Plugin_Interface{ // 自定义模型 $custom_model = new Typecho_Widget_Helper_Form_Element_Text('custom_model', NULL, NULL, _t('自定义配置文件地址'), _t('在这里填入一个模型 JSON 配置文件地址,可供使用外链模型,不填则使用插件目录下的模型')); $form -> addInput($custom_model); + + // 展现模式 + $custom_mode = new Typecho_Widget_Helper_Form_Element_Radio('custom_mode', + array( + 'static' => _t('静态'), + 'fixed' => _t('固定'), + 'draggable' => _t('可移动'), + ), + 'static', _t('展现模式'), _t('自定义看板娘的展现模式。静态模式将不启用按钮交互功能')); + $form -> addInput($custom_mode); + + // 是否在手机上隐藏 + $hidden = new Typecho_Widget_Helper_Form_Element_Radio('hidden', + array( + '0' => _t('关闭'), + '1' => _t('开启'), + ), + '0', _t('浏览体验'), _t('是否在手机版上隐藏看板娘')); + $form -> addInput($hidden); + + // 自定义文字配置 + $talk_content = new Typecho_Widget_Helper_Form_Element_Textarea('talk_content', NULL, '{}', _t('自定义提示内容'), _t('在这里填入你的自定义看板娘提示内容,如想保持默认,需要填写 "{}" 否则会导致插件无法运行')); + $form -> addInput($talk_content); + + // 自定义选择器配置 + $selector = new Typecho_Widget_Helper_Form_Element_Textarea('selector', NULL, '{}', _t('自定义内容选择器'), _t('在这里填入部分功能所用到的自定义选择器,如不想启用此类功能,需要填写 "{}" 否则会导致插件无法运行')); + $form -> addInput($selector); } /* 个人用户的配置方法 */ @@ -80,36 +107,65 @@ class Pio_Plugin implements Typecho_Plugin_Interface{ /* 插件实现方法 */ public static function header(){ + echo "\n"; $pos = Typecho_Widget::widget('Widget_Options') -> Plugin('Pio') -> position; - echo ""; + echo ""; } public static function footer(){ - $height = Typecho_Widget::widget('Widget_Options') -> Plugin('Pio') -> custom_height; - $width = Typecho_Widget::widget('Widget_Options') -> Plugin('Pio') -> custom_width; + // 生成画布 + function getCanvas(){ + $height = Typecho_Widget::widget('Widget_Options') -> Plugin('Pio') -> custom_height; + $width = Typecho_Widget::widget('Widget_Options') -> Plugin('Pio') -> custom_width; - if($height && $width){ - echo ""; - } - else if($height){ - echo ""; - } - else if($width){ - echo ""; - } - else{ - echo ""; + if(!$width){ $width = 280; } + if(!$height){ $height = 250; } + + return ""; } - echo "" . "\n"; + // 生成载入器 + function getLoader(){ + $config = array(); + $plug = Typecho_Widget::widget('Widget_Options') -> Plugin('Pio'); - if(Typecho_Widget::widget('Widget_Options') -> Plugin('Pio') -> custom_model){ - echo "". "\n"; - } - else if(Typecho_Widget::widget('Widget_Options') -> Plugin('Pio') -> choose_models){ - echo "". "\n"; - } - else{ - echo "". "\n"; + if($plug -> custom_model){ + $model = $plug -> custom_model; + } + else if($plug -> choose_models){ + $model = Helper::options() -> pluginUrl . "/Pio/models/" . $plug -> choose_models . "/model.json"; + } + else{ + $model = Helper::options() -> pluginUrl . "/Pio/models/pio/model.json"; + } + + $config["mode"] = $plug -> custom_mode; + $config["hidden"] = $plug -> hidden == 1 ? true : false; + + $config["model"] = array(); + $config["model"][0] = $model; + $config["content"] = json_decode($plug -> talk_content, true); + $config["selector"] = json_decode($plug -> selector, true); + + return ''; } + + $canvas = getCanvas(); + $loader = getLoader(); + + echo <<< Pio +
+
+ + + + +
+ $canvas +
+Pio; + + echo "" . "\n"; + echo "" . "\n"; + echo $loader; } } \ No newline at end of file diff --git a/README.md b/README.md index 660d600..6ee14ce 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # Pio -一个简易的 Live2D 插件,供 Typecho 使用。 +一个支持换模型的 Live2D 插件,供 Typecho 使用。 + +本插件不存在任何依赖的样式和库,在后续版本当中我会逐渐实现一些交互功能。 ## 使用方法 1. Star 本项目 @@ -7,12 +9,11 @@ 3. 将插件文件夹重命名为 `Pio` 4. 上传本插件,并放置在 `usr/plugins/` 目录下 5. 登录你的 Typecho 后台,并进行安装即可食用~ -6. 如果你有自己的模型,可以在插件设置更改,默认名称应该为 `model.json` +6. 如果你有自己的模型,可以将模型放在 `models` 目录下,并在插件设置里选择(需要确认模型的配置文件名称是否为 `model.json`)。或是在插件设置填写一个绝对链接 +7. 保罗自己搭建了一个模型资源收集站点 - [梦象](https://mx.paugram.com),你可以在这里下载到更多的模型,如果你有能力的话欢迎为我提供更多资源~ ## 项目故事 -自 [Jad](https://github.com/journey-ad) 发了一篇关于 Live2D 的教程之后,看到各位大佬的博客逐渐增加了这个小挂件。我一直盼望自己的博客也增加一个,而之前一直想找个机会试水一下 Typecho 的插件开发,于是今天就抽时间做出来了。 - -目前本插件不存在任何依赖的样式和库。在新版本当中我会逐渐实现一些交互功能,而不需要任何的库。 +详见我的博文:[给你的博客增加动态看板娘](https://paugram.com/coding/add-poster-girl-with-plugin.html) ## 开源协议 由于原项目使用 GPL 2.0 协议,故本项目也采用相同的开源协议进行授权。 diff --git a/l2d.js b/static/l2d.js similarity index 100% rename from l2d.js rename to static/l2d.js diff --git a/static/pio.css b/static/pio.css new file mode 100644 index 0000000..f22c06d --- /dev/null +++ b/static/pio.css @@ -0,0 +1,82 @@ +/* ---- + +# Pio Plugin +# By: Dreamer-Paul +# Last Update: 2018.10.12 + +一个支持换模型的 Live2D 插件,供 Typecho 使用。 + +本代码为奇趣保罗原创,并遵守 MIT 开源协议。欢迎访问我的博客:https://paugram.com + +---- */ + +.pio-container{ + left: 0; + bottom: 0; + position: fixed; + user-select: none; +} +.pio-container.active{ cursor: move } +.pio-container.static{ pointer-events: none } + +.pio-container .action-menu{ + top: 3em; + right: 0; + opacity: 0; + position: absolute; + transition: opacity .3s; +} +.pio-container:hover .action-menu{ opacity: 1 } + +.pio-container .action-menu span{ + color: #fff; + width: 1.5em; + height: 1.5em; + display: block; + cursor: pointer; + text-align: center; + border-radius: 66%; + margin-bottom: .5em; + border: 1px solid #666; + background: #fff center/70% no-repeat; +} +.pio-container .action-menu .home{ + background-image: url(data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMTAyNCAxMDI0IiB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTg5My43IDUwNS45SDEyOS4zYy0xMyAwLTI0LjgtNy45LTI5LjgtMTkuOS01LTEyLTIuMi0yNS45IDctMzUuMmwzMDctMzA3YzI2LjEtMjYuMSA2MC45LTQwLjUgOTgtNDAuNXM3MS45IDE0LjQgOTggNDAuNWwzMDcgMzA3YzkuMiA5LjIgMTIgMjMuMSA3IDM1LjItNSAxMi4xLTE2LjcgMTkuOS0yOS44IDE5Ljl6TTY3My4yIDkxOS45aC0zMS41Yy0xNy44IDAtMzIuMy0xNC40LTMyLjMtMzIuM3YtNzcuNGMwLTIzLjEtMTguOC00Mi4xLTQxLjktNDIuNC0yMi4zIDAuMy00MS4xIDE5LjMtNDEuMSA0Mi40djc3LjRjMCAxNy44LTE0LjQgMzIuMy0zMi4zIDMyLjNIMzQ5LjhjLTcwLjkgMC0xMjguNy02My43LTEyOC43LTE0MS45VjU4MS45YzAtMTcuOCAxNC40LTMyLjMgMzIuMy0zMi4zaDUxNi4yYzE3LjggMCAzMi4zIDE0LjQgMzIuMyAzMi4zVjc3OGMtMC4xIDc4LjMtNTcuOCAxNDEuOS0xMjguNyAxNDEuOXoiPjwvcGF0aD48L3N2Zz4=); +} +.pio-container .action-menu .close{ + background-image: url(data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMTAyNCAxMDI0IiB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTE1NS4yNTIgOTQzLjgyNWMtMTkuMjEzIDAtMzguNDI5LTcuMzMyLTUzLjA4OS0yMS45ODgtMjkuMzE3LTI5LjMyMS0yOS4zMTctNzYuODU1IDAtMTA2LjE3NWw3MTMuNDk0LTcxMy40OTRjMjkuMzE3LTI5LjMyMSA3Ni44NTMtMjkuMzIxIDEwNi4xNzUgMCAyOS4zMTcgMjkuMzE3IDI5LjMxNyA3Ni44NTUgMCAxMDYuMTc1bC03MTMuNDk0IDcxMy40OTRjLTE0LjY2IDE0LjY2LTMzLjg3NCAyMS45ODgtNTMuMDg5IDIxLjk4OHoiIGZpbGw9IiI+PC9wYXRoPjxwYXRoIGQ9Ik04NjguNzQ5IDk0My44MjRjLTE5LjIxMyAwLTM4LjQyOC03LjMzMi01My4wODktMjEuOTg4bC03MTMuNDk0LTcxMy40OTNjLTI5LjMxNy0yOS4zMTctMjkuMzE3LTc2Ljg1NyAwLTEwNi4xNzUgMjkuMzE2LTI5LjMxNyA3Ni44NTUtMjkuMzIxIDEwNi4xNzQgMGw3MTMuNDk0IDcxMy40OTJjMjkuMzE3IDI5LjMyMSAyOS4zMTcgNzYuODU1IDAgMTA2LjE3NS0xNC42NTcgMTQuNjYxLTMzLjg3MSAyMS45OTMtNTMuMDg3IDIxLjk5M3oiIGZpbGw9IiI+PC9wYXRoPjwvc3ZnPg==); +} +.pio-container .action-menu .skin{ + background-image: url(data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMTAyNCAxMDI0IiB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTk2NS45MTEgMzEwLjUzMWwtMTc0LjQtMTc0LjM5OGMtMTMuMDIyLTEzLjAyMS0zMC45MzMtMTkuNjQ5LTQ5LjM4MS0xOC4yMjgtMS43NC0wLjE1LTMuNDIyLTAuMjI0LTUuMDctMC4yMjRsLTkyLjkxNCAwLTYuNTE3IDMuNjI1Yy0zNC40MjEgMTkuMTQ2LTc4LjM0MSAyOS42ODktMTIzLjY2OCAyOS42ODktNDUuMzI4IDAtODkuMjQ2LTEwLjU0My0xMjMuNjY3LTI5LjY4OWwtNi41MTgtMy42MjVMMjkwLjg2IDExNy42ODFjLTIzLjY5MSAwLTQ0Ljk4NiAxMi45MjQtNTUuOTk1IDMzLjQ1MUw2Mi40NzcgMzIzLjUyMWMtMTEuOSAxMS44OTktMTguNDU0IDI3LjcyLTE4LjQ1NCA0NC41NDggMCAxNi44MjkgNi41NTQgMzIuNjQ5IDE4LjQ1MyA0NC41NDlsMTI1Ljk1MyAxMjUuOTU1YzEwLjU0IDEwLjUzOCAyNC4xNTcgMTYuODc4IDM4LjgyNiAxOC4xODFsMCAzMDQuMzk5YzAgMzUuMDczIDI4LjUzMyA2My42MDYgNjMuNjA0IDYzLjYwNmw0NDYuMTk5IDBjMzUuMDc0IDAgNjMuNjA3LTI4LjUzMyA2My42MDctNjMuNjA2bC0wLjAwMS0zMTcuMzQ1YzE0Ljg0NC0xLjIxMSAyOC42MzktNy41NzcgMzkuMjg4LTE4LjIyNEw5NjUuOTEgMzk5LjYyOEM5OTAuNDc1IDM3NS4wNjQgOTkwLjQ3NSAzMzUuMDk1IDk2NS45MTEgMzEwLjUzMXoiPjwvcGF0aD48L3N2Zz4=); +} +.pio-container .action-menu .info{ + background-image: url(data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMTAyNCAxMDI0IiB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTY4Mi45IDgyNS45SDI2Ny44Yy0yMS44IDAtMzkuNS0xNy43LTM5LjUtMzkuNXMxNy43LTM5LjUgMzkuNS0zOS41aDQxNS4xYzIxLjggMCAzOS41IDE3LjcgMzkuNSAzOS41cy0xNy43IDM5LjUtMzkuNSAzOS41ek04NjQuNyAxMDAuNGMtMTguNSAzLjctMzEuMyAyMC45LTMxLjMgMzkuN3Y2NDUuOGMwIDQ4LTM4LjkgODctODcgODdIMjE5LjNjLTE2LjQgMC0yOS42LTEzLjMtMjkuNi0yOS42VjczMi43YzAtMTYuMSAxMy4xLTI5LjIgMjkuMi0yOS4yaDM3NS45Yzg4LjEgMCAxNTkuNS03MS40IDE1OS41LTE1OS41VjE4NS41YzAtNjYuMi01My43LTExOS45LTExOS45LTExOS45aC00MDRjLTY2LjIgMC0xMTkuOSA1My43LTExOS45IDExOS45djY1Ny44YzAgNjAgNDguNyAxMDguNyAxMDguNyAxMDguN2g1MjcuMWM5MS43IDAgMTY2LjEtNzQuMyAxNjYuMS0xNjYuMVYxMzkuMWMwLjEtMjQuNi0yMi4yLTQzLjktNDcuNy0zOC43eiI+PC9wYXRoPjwvc3ZnPg==); +} +.pio-container .dialog{ + top: -2em; + left: 1em; + right: 1em; + opacity: 0; + z-index: -1; + font-size: .8em; + background: #fff; + padding: .75em 1em; + border-radius: 1em; + visibility: hidden; + position: absolute; + word-break: break-all; + border: 1px solid #eee; + transition: opacity .3s, visibility .3s; +} +.pio-container .dialog.active{ + opacity: 1; + visibility: visible; +} + +#pio{ vertical-align: middle } + +@media screen and (max-width: 768px){ + #pio{ width: 8em } + .pio-container{ pointer-events: none } + .pio-container.hidden, .pio-container .action-menu, .pio-container .dialog{ display: none } +} \ No newline at end of file diff --git a/static/pio.js b/static/pio.js new file mode 100644 index 0000000..a2f0040 --- /dev/null +++ b/static/pio.js @@ -0,0 +1,203 @@ +/* ---- + +# Pio Plugin +# By: Dreamer-Paul +# Last Update: 2018.10.12 + +一个支持换模型的 Live2D 插件,供 Typecho 使用。 + +本代码为奇趣保罗原创,并遵守 MIT 开源协议。欢迎访问我的博客:https://paugram.com + +---- */ + +var poster_girl = function (prop) { + var current = { + idol: 0, + canvas: document.getElementById("pio"), + body: document.getElementsByClassName("pio-container")[0], + root: document.location.protocol +'//' + document.location.hostname +'/' + }; + + var elements = { + home: current.body.getElementsByClassName("home")[0], + skin: current.body.getElementsByClassName("skin")[0], + info: current.body.getElementsByClassName("info")[0], + close: current.body.getElementsByClassName("close")[0] + }; + + var dialog = document.createElement("div"); + dialog.className = "dialog"; + current.body.appendChild(dialog); + + /* - 方法 */ + var modules = { + // 更换模型 + idol: function () { + current.idol < (prop.model.length - 1) ? current.idol++ : current.idol = 0; + return current.idol; + }, + // 随机内容 + rand: function (arr) { + return arr[Math.floor(Math.random() * arr.length + 1) - 1]; + }, + // 创建对话框方法 + render: function (text) { + if(text.constructor === Array){ + dialog.innerText = modules.rand(text); + } + else if(text.constructor === String){ + dialog.innerText = text; + } + else{ + dialog.innerText = "输入内容出现问题了 X_X"; + } + + dialog.classList.add("active"); + + clearTimeout(this.t); + this.t = setTimeout(function () { + dialog.classList.remove("active"); + }, 3000); + }, + // 移除方法 + destroy: function () { + current.body.parentNode.removeChild(current.body); + } + }; + + /* - 提示操作 */ + var action = { + // 欢迎 + welcome: function () { + if(document.referrer !== "" && document.referrer.indexOf(current.root) === -1){ + var referrer = document.createElement('a'); + referrer.href = document.referrer; + prop.content.welcome && prop.content.welcome[1] ? modules.render(prop.content.welcome[1].replace(/%t/, "“" + referrer.hostname + "”")) : modules.render("欢迎来自 “" + document.referrer + "” 的朋友!"); + } + else{ + prop.content.welcome && prop.content.welcome[0] ? modules.render(prop.content.welcome[0]) : modules.render("欢迎来到保罗的小窝!"); + } + }, + // 文章 + article: function () { + if(prop.selector.articles){ + var a = document.querySelectorAll(prop.selector.articles), b; + prop.content.article ? b = prop.content.article : b = "想阅读 %t 吗?"; + + for(var i = 0; i < a.length; i++){ + a[i].onmouseover = function () { + modules.render(b.replace(/%t/, "“" + this.innerText + "”")); + } + } + } + }, + // 触摸 + touch: function () { + if(prop.content.touch){ + current.canvas.onclick = function () { + modules.render(prop.content.touch); + } + } + else{ + current.canvas.onclick = function () { + modules.render(["你在干什么?", "再摸我就报警了!", "HENTAI!", "你够了喔!"]); + } + } + }, + // 右侧按钮 + buttons: function () { + // 返回首页 + if(elements.home){ + elements.home.onclick = function () { + location.href = current.root; + }; + elements.home.onmouseover = function () { + prop.content.home ? modules.render(prop.content.home) : modules.render("点击这里回到首页!"); + }; + } + + // 更换模型 + if(elements.skin){ + elements.skin.onclick = function () { + loadlive2d("pio", prop.model[modules.idol()]); + prop.content.skin && prop.content.skin[1] ? modules.render(prop.content.skin[1]) : modules.render("新衣服真漂亮~"); + }; + elements.skin.onmouseover = function () { + prop.content.skin && prop.content.skin[0] ? modules.render(prop.content.skin[0]) : modules.render("想看看我的新衣服吗?"); + }; + } + + // 关于我 + if(elements.info){ + elements.info.onclick = function () { + window.open("https://paugram.com/coding/add-poster-girl-with-plugin.html"); + }; + elements.info.onmouseover = function () { + modules.render("想了解更多关于我的信息吗?"); + }; + } + + // 关闭看板娘 + if(elements.close){ + elements.close.onclick = function () { + modules.destroy(); + }; + elements.close.onmouseover = function () { + prop.content.close ? modules.render(prop.content.close) : modules.render("QWQ 下次再见吧~"); + }; + + document.cookie = "posterGirl=false;" + "path=/"; + } + } + }; + + /* - 运行 */ + var begin = { + static: function () { + action.welcome(); action.article(); + current.body.classList.add("static"); + }, + fixed: function () { + action.welcome(); action.article(); action.touch(); action.buttons(); + }, + draggable: function () { + action.welcome(); action.article(); action.touch(); action.buttons(); + + var body = current.body; + body.onmousedown = function () { + var location = { + x: event.clientX - this.offsetLeft, + y: event.clientY - this.offsetTop + }; + + function move(e) { + body.classList.add("active"); + body.style.left = (event.clientX - location.x) + 'px'; + body.style.top = (event.clientY - location.y) + 'px'; + } + + document.addEventListener("mousemove", move); + document.addEventListener("mouseup", function () { + body.classList.remove("active"); + document.removeEventListener("mousemove", move); + }); + }; + } + }; + + // 判断模式并运行 + switch (prop.mode){ + case "static": begin.static(); break; + case "fixed": begin.fixed(); break; + case "draggable": begin.draggable(); break; + } + + if(prop.hidden === true){ current.body.classList.add("hidden") } + + loadlive2d("pio", prop.model[0]); +}; + +// 请保留版权说明 +if (window.console && window.console.log) { + console.log("%c Pio %c https://paugram.com ","color: #fff; margin: 1em 0; padding: 5px 0; background: #673ab7;","margin: 1em 0; padding: 5px 0; background: #efefef;"); +} \ No newline at end of file