给博客添加能动的看板娘(Live2D)-将其添加到网页上吧

药水制作师

前言

在上一篇文章里,我介绍了Live2D的一些技术背景和从手游「药水制作师」中提取模型的方法步骤。在这篇文章里,我将接着讲述如何利用WebGL SDK将其添加到网页上 ——就像本站一样。

cPlayerOptions.push({"id":4,"list":[{"artist":"\u6e05\u6d66\u590f\u5b9f","name":"\u65c5\u306e\u9014\u4e2d","image":"\/\/p1.music.126.net\/4BgAnUbCDFex3m4z-hWULA==\/2509085534622060.jpg?param=128x128","url":"https:\/\/imjad.cn\/action\/cplayerapi?get=url&id=26220167","lyric":"[by:\u55b7\u6db2]\n[ti:\u65c5\u306e\u9014\u4e2d]\n[ar:\u6e05\u6d66\u590f\u5b9f]\n[al:TV\u52a8\u753b\u300c\u72fc\u4e0e\u9999\u8f9b\u6599\u300d\u7247\u5934\u66f2]\n[00:26.590]\u305f\u3060\u3072\u3068\u308a \u8ff7(\u307e\u3088)\u3044\u8fbc(\u3053)\u3080\u65c5(\u305f\u3073)\u306e\u4e2d(\u306a\u304b)\u3067\n[00:35.000]\u5fc3(\u3053\u3053\u308d)\u3060\u3051\u5f77\u5fa8(\u3055\u307e\u3088)\u3063\u3066\u7acb(\u305f)\u3061\u5c3d(\u3064\u304f)\u3057\u305f\n[00:43.320]\u3067\u3082\u4eca(\u3044\u307e)\u306f \u9060(\u3068\u304a)\u304f\u307e\u3067 \u6b69(\u3042\u308b)\u304d\u51fa(\u3060)\u305b\u308b\n[00:51.710]\u305d\u3046\u541b(\u304d\u307f)\u3068 \u3053\u306e\u9053(\u307f\u3061)\u3067 \u51fa\u4f1a(\u3067\u3042)\u3063\u3066\u304b\u3089\n[00:59.770]\n[01:00.720]\u65c5\u4eba(\u305f\u3073\u3073\u3068)\u305f\u3061\u304c\u6b4c(\u3046\u305f)\u3046\n[01:04.940]\u898b\u77e5(\u307f\u3057)\u3089\u306c\u6b4c(\u3046\u305f)\u3082\n[01:08.450]\u61d0(\u306a\u3064)\u304b\u3057\u304f\u8074(\u304d)\u3053\u3048\u3066\u304f\u308b\u3088\n[01:12.990]\u305f\u3060\u541b(\u304d\u307f)\u3068\u3044\u308b\u3068\n[01:17.540]\n[01:18.910]\u5922\u898b(\u3086\u3081\u307f)\u305f\u4e16\u754c(\u305b\u304b\u3044)\u304c \u3069\u3053\u304b\u306b \u3042\u308b\u306a\u3089\n[01:27.210]\u63a2(\u3055\u304c)\u3057\u306b \u884c(\u3086)\u3053\u3046\u304b \u98a8(\u304b\u305c)\u306e\u3080\u3053\u3046\u3078\n[01:35.610]\u51cd(\u3044)\u3066\u3064\u304f\u591c\u660e(\u3088\u3042)\u3051\u306e \u6e07(\u304b\u308f)\u3044\u305f\u771f\u663c(\u307e\u3072\u308b)\u306e\n[01:43.950]\u3075\u308b\u3048\u308b\u95c7\u591c(\u3084\u307f\u3088)\u306e \u679c(\u306f)\u3066\u3092\u898b(\u307f)\u306b\u884c(\u3086)\u3053\u3046\n[01:53.130]\n[01:55.130]\n[02:07.110]\n[02:09.170]\u5bc2(\u3055\u3073)\u3057\u3055\u3092\u77e5(\u3057)\u3063\u3066\u3044\u308b \u541b(\u304d\u307f)\u306e\u77b3(\u3072\u3068\u307f)\n[02:17.540]\u307e\u3070\u305f\u3044\u3066 \u305d\u306e\u8272(\u3044\u308d)\u3092 \u6620(\u3046\u3064)\u3059\u304b\u3089\n[02:25.810]\n[02:26.570]\u9ad8(\u305f\u304b)\u304f\u7a7a(\u305d\u3089)\u307e\u3067\u98db(\u3068)\u3093\u3067\n[02:30.770]\u4e09\u65e5\u6708(\u307f\u304b\u3065\u304d)\u306b\u306a\u308b\n[02:34.320]\u30cf\u30c3\u30ab\u8272(\u3044\u308d)\u306e\u661f(\u307b\u3057)\u306f\u304d\u3063\u3068\n[02:39.160]\u6d99(\u306a\u307f\u3060)\u306e\u304b\u3051\u3089\n[02:43.430]\n[02:45.480]\u6771(\u3072\u304c\u3057)\u306e\u56fd(\u304f\u306b)\u306e\u6e2f(\u307f\u306a\u3068) \u897f(\u306b\u3057)\u306e\u6d77\u8fba(\u3046\u307f\u3079)\n[02:53.800]\u6697(\u304f\u3089)\u3044\u68ee(\u3082\u308a)\u3067 \u5357(\u307f\u306a\u307f)\u306e\u8857(\u307e\u3061) \u91d1(\u304d\u3093)\u306e\u5854(\u3068\u3046)\n[03:02.150]\u5317(\u304d\u305f)\u306e\u4e18(\u304a\u304b) \u6c34(\u307f\u305a)\u306b\u63fa(\u3086)\u308c\u3066\u305f\u540c(\u304a\u306a)\u3058\u6708(\u3064\u304d)\u304c\n[03:14.730]\n[03:18.160]\u5dee(\u3055)\u3057\u51fa(\u3060)\u3059\u305d\u306e\u624b(\u3066)\u3092 \u3064\u306a\u3044\u3067\u3044\u3044\u306a\u3089\n[03:26.510]\u3069\u3053\u307e\u3067\u884c(\u3086)\u3053\u3046\u304b \u541b(\u304d\u307f)\u3068\u4e8c\u4eba(\u3075\u305f\u308a)\u3067\n[03:34.990]\u3069\u3053\u3078\u3082\u884c(\u3086)\u3051\u308b\u3088 \u307e\u3060\u898b(\u307f)\u306c\u4e16\u754c(\u305b\u304b\u3044)\u306e\n[03:43.300]\u3056\u308f\u3081\u304d \u9999(\u304b\u304a)\u308a\u3092 \u62b1(\u3060)\u304d\u3057\u3081\u306b\u884c(\u3086)\u3053\u3046\n","transLyric":"[by:\u6708\u68ee\u97f3]\n[00:26.590]\u4e00\u4e2a\u4eba \u8d70\u5728\u4ee4\u4eba\u8ff7\u5931\u7684\u65c5\u9014\n[00:35.000]\u4e00\u9897\u5fc3 \u5374\u5f77\u5fa8\u7740\u5446\u7acb\u5728\u539f\u5904\n[00:43.320]\u800c\u5982\u4eca \u6211\u5df2\u7ecf\u80fd\u591f\u8d70\u5411\u8fdc\u65b9\n[00:51.710]\u662f\u56e0\u4e3a \u6211\u5728\u65c5\u9014\u4e2d\u9047\u89c1\u4e86\u4f60\n[01:00.720]\u5373\u4f7f\u662f\u65c5\u9014\u4e2d\u7684\u4eba\u4eec\n[01:04.940]\u5531\u8d77\u4e86\u964c\u751f\u7684\u6b4c\u8c23\n[01:08.450]\u6211\u4e5f\u89c9\u5f97\u4ee4\u4eba\u6000\u5ff5\n[01:12.990]\u53ea\u8981\u6709\u4f60\u966a\u4f34\u8eab\u8fb9\n[01:18.910]\u5982\u679c\u68a6\u4e2d\u7684\u4e16\u754c \u5c31\u5728\u4e16\u4e0a\u7684\u67d0\u5904\n[01:27.210]\u8ba9\u6211\u4eec\u4e00\u540c\u5bfb\u89c5 \u53bb\u5f80\u98ce\u7684\u53e6\u4e00\u65b9\n[01:35.610]\u65e0\u8bba\u5bd2\u51b7\u7684\u9ece\u660e \u6291\u6216\u5e72\u6e34\u7684\u6b63\u5348\n[01:43.950]\u54ea\u6015\u98a4\u6817\u7684\u6697\u591c \u613f\u4e0e\u4f60\u4e00\u540c\u8d70\u8fc7\n[02:09.170]\u4f60\u7684\u53cc\u773c \u60f3\u5fc5\u65e9\u5df2\u89c1\u60ef\u4e86\u5b64\u72ec\n[02:17.540]\u56e0\u4e3a\u5b83\u4eec \u603b\u662f\u95ea\u7740\u5bc2\u5bde\u7684\u989c\u8272\n[02:26.570]\u6211\u613f\u98de\u4e0a\u9ad8\u9ad8\u7684\u591c\u7a7a\n[02:30.770]\u4e3a\u4f60\u5316\u4f5c\u4e00\u5f2f\u65b0\u6708\n[02:34.320]\u90a3\u6ee1\u5929\u8584\u8377\u8272\u7684\u661f\u8fb0\n[02:39.160]\u4e00\u5b9a\u5c31\u662f\u6211\u7684\u773c\u6cea\n[02:45.480]\u8d70\u8fc7\u4e1c\u65b9\u56fd\u5ea6\u7684\u6d77\u6e2f \u897f\u8fb9\u7684\u6d77\u6ee9\n[02:53.800]\u8d70\u8fc7\u9ed1\u6697\u68ee\u6797 \u5357\u65b9\u57ce\u5e02 \u91d1\u8272\u7684\u5854\n[03:02.150]\u5317\u56fd\u5c71\u4e18 \u6c34\u4e2d\u8361\u6f3e\u7740\u540c\u6837\u7684\u6708\u5149\n[03:18.160]\u5982\u679c\u80fd\u5141\u8bb8\u6211\u63e1\u4f4f \u4f60\u9012\u7ed9\u6211\u7684\u624b\n[03:26.510]\u4e0d\u77e5\u9053\u6211\u80fd\u591f\u4e0e\u4f60 \u8d70\u5230\u4ec0\u4e48\u5730\u65b9\n[03:34.990]\u4e0e\u4f60\u8d70\u904d\u5929\u6daf\u6d77\u89d2 \u8d70\u5411\u672a\u77e5\u4e16\u754c\n[03:43.300]\u53bb\u7d27\u7d27\u62e5\u62b1 \u6211\u4eec\u4e00\u8def\u9082\u9005\u7684\u82ac\u82b3\n","id":"26220167"}]});

准备SDK

为了效率最大化,不用把有限的时间投入到与bug无限的斗争和踩坑中;为了不重复造轮子;为了爱与和平 就是因为懒,我们不使用官方的SDK,而是用@EYHN大佬封装好的库来加载并显示模型。相比较官方版本而言,仅需一条命令即可进行模型的加载,无疑方便许多。

但对于「药水制作师」这款游戏的模型来说,上面所说的库还需经过一些修改。

修改原因
由于作者的特殊考量,#fea64e4没有实现对鼠标点击事件(Event)的处理,而是在鼠标移动时触发点击事件,我对此作了修改。
此外模型动作里有眨眼的动作,与默认的眨眼动作存在冲突,我注释了默认的眨眼动作
有趣的地方:默认的眨眼动作眼睛开合度给的上下限值为-1~1,但「药水制作师」的眼睛开合度为0.9以上时眼睛会消失
少女盲目分析中
Pio眼睛的消失
「药水制作师」带有一个久置动作,为了利用好这个动作,我通过监听mouseout增加了对其的支持。不用mouseleave的原因是Firefox对于documentmouseleave事件响应实现与Chrome不一致
同样,「药水制作师」带有数个点击动作,而模型中并不存在HIT_AREA,所以直接点击无效。我实现了通过model.json中提供的参数(Parameter)手动定义HIT_AREA。

基于commit #fea64e4修改,修改内容:

修复移动鼠标会触发点击事件的问题 增加鼠标点击事件 移除自带的眨眼动作 增加久置动作与事件支持 增加自定义HIT_AREA的方法 增加保存当前帧到文件的功能

由于原项目使用了GPL v2开源协议,修改后的代码已开源至GitHub,若想修改请参考项目hexo-helper-live2d ,对Node.js和JavaScript了解不深,功能实现的比较笨,希望有dalao pr?

鉴于该项目仍在活跃开发中,我修改的版本可能会随时间变化而过时

构建(Build)好的版本可以在这里下载

提示功能(可选)

这部分直接抄自之前的浮动小人,做了部分修改,非必须项,须jQuery支持

若不启用此部分内容,下一步应作相应修改
此处代码不可直接套用,应根据自身情况进行修改

脚本(Script)

将以下内容保存为waifu-tips.js,放至相应目录

function render(template, context) {

    var tokenReg = /(\\)?\{([^\{\}\\]+)(\\)?\}/g;

    return template.replace(tokenReg, function (word, slash1, token, slash2) {
        if (slash1 || slash2) {  
            return word.replace('\\', '');
        }

        var variables = token.replace(/\s/g, '').split('.');
        var currentObject = context;
        var i, length, variable;

        for (i = 0, length = variables.length; i < length; ++i) {
            variable = variables[i];
            currentObject = currentObject[variable];
            if (currentObject === undefined || currentObject === null) return '';
        }
        return currentObject;
    });
}
String.prototype.render = function (context) {
    return render(this, context);
};

var re = /x/;
console.log(re);
re.toString = function() {
    showMessage('哈哈,你打开了控制台,是想要看看我的秘密吗?', 5000);
    return '';
};

$(document).on('copy', function (){
    showMessage('你都复制了些什么呀,转载要记得加上出处哦', 5000);
});

$.ajax({
    cache: true,
    url: "path/to/waifu-tips.json",
    dataType: "json",
    success: function (result){
        $.each(result.mouseover, function (index, tips){
            $(document).on("mouseover", tips.selector, function (){
                var text = tips.text;
                if(Array.isArray(tips.text)) text = tips.text[Math.floor(Math.random() * tips.text.length + 1)-1];
                text = text.render({text: $(this).text()});
                showMessage(text, 3000);
            });
        });
        $.each(result.click, function (index, tips){
            $(document).on("click", tips.selector, function (){
                var text = tips.text;
                if(Array.isArray(tips.text)) text = tips.text[Math.floor(Math.random() * tips.text.length + 1)-1];
                text = text.render({text: $(this).text()});
                showMessage(text, 3000);
            });
        });
    }
});

(function (){
    var text;
    if(document.referrer !== ''){
        var referrer = document.createElement('a');
        referrer.href = document.referrer;
        text = 'Hello! 来自 <span style="color:#0099cc;">' + referrer.hostname + '</span> 的朋友';
        var domain = referrer.hostname.split('.')[1];
        if (domain == 'baidu') {
            text = 'Hello! 来自 百度搜索 的朋友<br>你是搜索 <span style="color:#0099cc;">' + referrer.search.split('&wd=')[1].split('&')[0] + '</span> 找到的我吗?';
        }else if (domain == 'so') {
            text = 'Hello! 来自 360搜索 的朋友<br>你是搜索 <span style="color:#0099cc;">' + referrer.search.split('&q=')[1].split('&')[0] + '</span> 找到的我吗?';
        }else if (domain == 'google') {
            text = 'Hello! 来自 谷歌搜索 的朋友<br>欢迎阅读<span style="color:#0099cc;">『' + document.title.split(' - ')[0] + '』</span>';
        }
    }else {
        if (window.location.href == 'https://imjad.cn/') { //如果是主页
            var now = (new Date()).getHours();
            if (now > 23 || now <= 5) {
                text = '你是夜猫子呀?这么晚还不睡觉,明天起的来嘛';
            } else if (now > 5 && now <= 7) {
                text = '早上好!一日之计在于晨,美好的一天就要开始了';
            } else if (now > 7 && now <= 11) {
                text = '上午好!工作顺利嘛,不要久坐,多起来走动走动哦!';
            } else if (now > 11 && now <= 14) {
                text = '中午了,工作了一个上午,现在是午餐时间!';
            } else if (now > 14 && now <= 17) {
                text = '午后很容易犯困呢,今天的运动目标完成了吗?';
            } else if (now > 17 && now <= 19) {
                text = '傍晚了!窗外夕阳的景色很美丽呢,最美不过夕阳红~';
            } else if (now > 19 && now <= 21) {
                text = '晚上好,今天过得怎么样?';
            } else if (now > 21 && now <= 23) {
                text = '已经这么晚了呀,早点休息吧,晚安~';
            } else {
                text = '嗨~ 快来逗我玩吧!';
            }
        }else {
            text = '欢迎阅读<span style="color:#0099cc;">『' + document.title.split(' - ')[0] + '』</span>';
        }
    }
    showMessage(text, 6000);
})();

window.setInterval(showHitokoto,30000);

function showHitokoto(){
    $.getJSON('https://api.imjad.cn/hitokoto/?cat=&charset=utf-8&length=28&encode=json',function(result){
        showMessage(result.hitokoto, 5000);
    });
}

function showMessage(text, timeout){
    if(Array.isArray(text)) text = text[Math.floor(Math.random() * text.length + 1)-1];
    console.log(text);
    $('.waifu-tips').stop();
    $('.waifu-tips').html(text).fadeTo(200, 1);
    if (timeout === null) timeout = 5000;
    hideMessage(timeout);
}
function hideMessage(timeout){
    $('.waifu-tips').stop().css('opacity',1);
    if (timeout === null) timeout = 5000;
    $('.waifu-tips').delay(timeout).fadeTo(200, 0);
}

文本

将以下内容保存为waifu-tips.json,放至相应目录

{
    "mouseover": [
        {
            "selector": ".container a[href^='http']",
            "text": ["要看看 <span style=\"color:#0099cc;\">{text}</span> 么?"]
        },
        {
            "selector": ".fui-home",
            "text": ["点击前往首页,想回到上一页可以使用浏览器的后退功能哦"]
        },
        {
            "selector": "#tor_show",
            "text": ["翻页比较麻烦吗,点击可以显示这篇文章的目录呢"]
        },
        {
            "selector": "#comment_go,.fui-chat",
            "text": ["想要去评论些什么吗?"]
        },
        {
            "selector": "#night_mode",
            "text": ["深夜时要爱护眼睛呀"]
        },
        {
            "selector": "#qrcode",
            "text": ["手机扫一下就能继续看,很方便呢"]
        },
        {
            "selector": ".comment_reply",
            "text": ["要吐槽些什么呢"]
        },
        {
            "selector": "#back-to-top",
            "text": ["回到开始的地方吧"]
        },
        {
            "selector": "#author",
            "text": ["该怎么称呼你呢"]
        },
        {
            "selector": "#mail",
            "text": ["留下你的邮箱,不然就是无头像人士了"]
        },
        {
            "selector": "#url",
            "text": ["你的家在哪里呢,好让我去参观参观"]
        },
        {
            "selector": "#textarea",
            "text": ["认真填写哦,垃圾评论是禁止事项"]
        },
        {
            "selector": ".OwO-logo",
            "text": ["要插入一个表情吗"]
        },
        {
            "selector": "#csubmit",
            "text": ["要[提交]^(Commit)了吗,首次评论需要审核,请耐心等待~"]
        },
        {
            "selector": ".ImageBox",
            "text": ["点击图片可以放大呢"]
        },
        {
            "selector": "input[name=s]",
            "text": ["找不到想看的内容?搜索看看吧"]
        },
        {
            "selector": ".previous",
            "text": ["去上一页看看吧"]
        },
        {
            "selector": ".next",
            "text": ["去下一页看看吧"]
        },
        {
            "selector": ".dropdown-toggle",
            "text": ["这里是菜单"]
        },
        {
            "selector": "c-player a.play-icon",
            "text": ["想要听点音乐吗"]
        },
        {
            "selector": "c-player div.time",
            "text": ["在这里可以调整<span style=\"color:#0099cc;\">播放进度</span>呢"]
        },
        {
            "selector": "c-player div.volume",
            "text": ["在这里可以调整<span style=\"color:#0099cc;\">音量</span>呢"]
        },
        {
            "selector": "c-player div.list-button",
            "text": ["<span style=\"color:#0099cc;\">播放列表</span>里都有什么呢"]
        },
        {
            "selector": "c-player div.lyric-button",
            "text": ["有<span style=\"color:#0099cc;\">歌词</span>的话就能跟着一起唱呢"]
        },
        {
            "selector": ".waifu #live2d",
            "text": ["干嘛呢你,快把手拿开", "鼠…鼠标放错地方了!"]
        }
    ],
    "click": [
        {
            "selector": ".waifu #live2d",
            "text": ["是…是不小心碰到了吧", "萝莉控是什么呀", "你看到我的小熊了吗", "再摸的话我可要报警了!⌇●﹏●⌇", "110吗,这里有个变态一直在摸我(ó﹏ò。)"]
        }
    ]
}

引入JS

修改header.php,加入以下内容以创建画布和提示框:

<div class="waifu">
    <div class="waifu-tips"></div>
    <canvas id="live2d" width="280" height="250" class="live2d"></canvas>
</div>

footer.php中加入以下内容:

<script async src="path/to/waifu-tips.js"></script>
<script src="path/to/live2d.js"></script>
<script type="text/javascript">
    loadlive2d("live2d", "path/to/model.json");
</script>

上一篇文章提到的model.json修改为以下内容

其中hit_areas_custom字段的head_xbody_x定义了头部和身体的HIT_AREA的左上角的坐标,head_ybody_y定义了右下角的坐标

坐标可通过启用DEBUG_MOUSE_LOG获取

{
    "version":"1.0.0",
    "model":"model.moc",
    "textures":[
        "textures/default-costume.png"
    ],
    "layout":{
        "center_x":0.0,
        "center_y":-0.05,
        "width":2.0
    },
    "hit_areas_custom":{
        "head_x":[-0.35, 0.6],
        "head_y":[0.19, -0.2],
        "body_x":[-0.3, -0.25],
        "body_y":[0.3, -0.9]
    },
    "motions":{
        "idle":[
            {"file":"motions/WakeUp.mtn"},
            {"file":"motions/Breath1.mtn"},
            {"file":"motions/Breath2.mtn"},
            {"file":"motions/Breath3.mtn"},
            {"file":"motions/Breath5.mtn"},
            {"file":"motions/Breath7.mtn"},
            {"file":"motions/Breath8.mtn"}
        ],
        "sleepy":[
            {"file":"motions/Sleeping.mtn"}
        ],
        "flick_head":[
            {"file":"motions/Touch Dere1.mtn"},
            {"file":"motions/Touch Dere2.mtn"},
            {"file":"motions/Touch Dere3.mtn"},
            {"file":"motions/Touch Dere4.mtn"},
            {"file":"motions/Touch Dere5.mtn"},
            {"file":"motions/Touch Dere6.mtn"}
        ],
        "tap_body":[
            {"file":"motions/Touch1.mtn"},
            {"file":"motions/Touch2.mtn"},
            {"file":"motions/Touch3.mtn"},
            {"file":"motions/Touch4.mtn"},
            {"file":"motions/Touch5.mtn"},
            {"file":"motions/Touch6.mtn"}
        ],
        "":[
            {"file":"motions/Breath1.mtn"},
            {"file":"motions/Breath2.mtn"},
            {"file":"motions/Breath3.mtn"},
            {"file":"motions/Breath4.mtn"},
            {"file":"motions/Breath5.mtn"},
            {"file":"motions/Breath6.mtn"},
            {"file":"motions/Breath7.mtn"},
            {"file":"motions/Breath8.mtn"},
            {"file":"motions/Fail.mtn"},
            {"file":"motions/Sleeping.mtn"},
            {"file":"motions/Success.mtn"},
            {"file":"motions/Sukebei1.mtn"},
            {"file":"motions/Sukebei2.mtn"},
            {"file":"motions/Sukebei3.mtn"},
            {"file":"motions/Touch Dere1.mtn"},
            {"file":"motions/Touch Dere2.mtn"},
            {"file":"motions/Touch Dere3.mtn"},
            {"file":"motions/Touch Dere4.mtn"},
            {"file":"motions/Touch Dere5.mtn"},
            {"file":"motions/Touch Dere6.mtn"},
            {"file":"motions/Touch1.mtn"},
            {"file":"motions/Touch2.mtn"},
            {"file":"motions/Touch3.mtn"},
            {"file":"motions/Touch4.mtn"},
            {"file":"motions/Touch5.mtn"},
            {"file":"motions/Touch6.mtn"},
            {"file":"motions/WakeUp.mtn"}
        ]
    }
}

增加样式(Style)

.waifu {
    position: fixed;
    bottom: 0;
    left: 0;
    z-index: 1;
    font-size: 0;
    transition: all .3s ease-in-out;
    -webkit-transform: translateY(3px);
    transform: translateY(3px);
}
.waifu:hover {
    -webkit-transform: translateY(0);
    transform: translateY(0);
}
@media (max-width: 768px) {
    .waifu {
        display: none;
    }
}
.waifu-tips {
    opacity: 0;
    width: 250px;
    height: 70px;
    margin: -20px 20px;
    padding: 5px 10px;
    border: 1px solid rgba(224, 186, 140, 0.62);
    border-radius: 12px;
    background-color: rgba(236, 217, 188, 0.5);
    box-shadow: 0 3px 15px 2px rgba(191, 158, 118, 0.2);
    font-size: 12px;
    text-overflow: ellipsis;
    overflow: hidden;
    position: absolute;
    animation-delay: 5s;
    animation-duration: 50s;
    animation-iteration-count: infinite;
    animation-name: shake;
    animation-timing-function: ease-in-out;
}
.waifu #live2d{
    position: relative;
}

@keyframes shake {
    2% {
        transform: translate(0.5px, -1.5px) rotate(-0.5deg);
    }

    4% {
        transform: translate(0.5px, 1.5px) rotate(1.5deg);
    }

    6% {
        transform: translate(1.5px, 1.5px) rotate(1.5deg);
    }

    8% {
        transform: translate(2.5px, 1.5px) rotate(0.5deg);
    }

    10% {
        transform: translate(0.5px, 2.5px) rotate(0.5deg);
    }

    12% {
        transform: translate(1.5px, 1.5px) rotate(0.5deg);
    }

    14% {
        transform: translate(0.5px, 0.5px) rotate(0.5deg);
    }

    16% {
        transform: translate(-1.5px, -0.5px) rotate(1.5deg);
    }

    18% {
        transform: translate(0.5px, 0.5px) rotate(1.5deg);
    }

    20% {
        transform: translate(2.5px, 2.5px) rotate(1.5deg);
    }

    22% {
        transform: translate(0.5px, -1.5px) rotate(1.5deg);
    }

    24% {
        transform: translate(-1.5px, 1.5px) rotate(-0.5deg);
    }

    26% {
        transform: translate(1.5px, 0.5px) rotate(1.5deg);
    }

    28% {
        transform: translate(-0.5px, -0.5px) rotate(-0.5deg);
    }

    30% {
        transform: translate(1.5px, -0.5px) rotate(-0.5deg);
    }

    32% {
        transform: translate(2.5px, -1.5px) rotate(1.5deg);
    }

    34% {
        transform: translate(2.5px, 2.5px) rotate(-0.5deg);
    }

    36% {
        transform: translate(0.5px, -1.5px) rotate(0.5deg);
    }

    38% {
        transform: translate(2.5px, -0.5px) rotate(-0.5deg);
    }

    40% {
        transform: translate(-0.5px, 2.5px) rotate(0.5deg);
    }

    42% {
        transform: translate(-1.5px, 2.5px) rotate(0.5deg);
    }

    44% {
        transform: translate(-1.5px, 1.5px) rotate(0.5deg);
    }

    46% {
        transform: translate(1.5px, -0.5px) rotate(-0.5deg);
    }

    48% {
        transform: translate(2.5px, -0.5px) rotate(0.5deg);
    }

    50% {
        transform: translate(-1.5px, 1.5px) rotate(0.5deg);
    }

    52% {
        transform: translate(-0.5px, 1.5px) rotate(0.5deg);
    }

    54% {
        transform: translate(-1.5px, 1.5px) rotate(0.5deg);
    }

    56% {
        transform: translate(0.5px, 2.5px) rotate(1.5deg);
    }

    58% {
        transform: translate(2.5px, 2.5px) rotate(0.5deg);
    }

    60% {
        transform: translate(2.5px, -1.5px) rotate(1.5deg);
    }

    62% {
        transform: translate(-1.5px, 0.5px) rotate(1.5deg);
    }

    64% {
        transform: translate(-1.5px, 1.5px) rotate(1.5deg);
    }

    66% {
        transform: translate(0.5px, 2.5px) rotate(1.5deg);
    }

    68% {
        transform: translate(2.5px, -1.5px) rotate(1.5deg);
    }

    70% {
        transform: translate(2.5px, 2.5px) rotate(0.5deg);
    }

    72% {
        transform: translate(-0.5px, -1.5px) rotate(1.5deg);
    }

    74% {
        transform: translate(-1.5px, 2.5px) rotate(1.5deg);
    }

    76% {
        transform: translate(-1.5px, 2.5px) rotate(1.5deg);
    }

    78% {
        transform: translate(-1.5px, 2.5px) rotate(0.5deg);
    }

    80% {
        transform: translate(-1.5px, 0.5px) rotate(-0.5deg);
    }

    82% {
        transform: translate(-1.5px, 0.5px) rotate(-0.5deg);
    }

    84% {
        transform: translate(-0.5px, 0.5px) rotate(1.5deg);
    }

    86% {
        transform: translate(2.5px, 1.5px) rotate(0.5deg);
    }

    88% {
        transform: translate(-1.5px, 0.5px) rotate(1.5deg);
    }

    90% {
        transform: translate(-1.5px, -0.5px) rotate(-0.5deg);
    }

    92% {
        transform: translate(-1.5px, -1.5px) rotate(1.5deg);
    }

    94% {
        transform: translate(0.5px, 0.5px) rotate(-0.5deg);
    }

    96% {
        transform: translate(2.5px, -0.5px) rotate(-0.5deg);
    }

    98% {
        transform: translate(-1.5px, -1.5px) rotate(-0.5deg);
    }

    0%, 100% {
        transform: translate(0, 0) rotate(0);
    }
}

如一切正常,刷新网页后,可爱的Pio就会出现在页面左下角,点击会播放不同的动作并有相应提示文字

参考

hexo-helper-live2d
live2d_src

文章来源:

Author:journey.ad
link:https://imjad.cn/archives/lab/add-dynamic-poster-girl-with-live2d-to-your-blog-02