live2d-widget 看板娘(改)

代码修改于 https://www.fghrsh.net/post/123.html
核心依赖 live2d.min.js 可以从 https://github.com/stevenjoezhang/live2d-widget下载,嫌速度慢的话,gitee上也有。

l i v e 2 d . t i p s . j s live2d.tips.js live2d.tips.js(对话框逻辑)

/** live2d 加载类 */
class Live2d_tips {
  constructor(config) {
    let { modelSrc, modelId, modelSite, actionTime, actionText, musicListId } =
      config;

    this.modelSrc = modelSrc;
    this.width = modelSite.width;
    this.height = modelSite.height;
    this.hOffset = modelSite.hOffset;
    this.vOffset = modelSite.vOffset;
    this.actionTime = actionTime;
    this.actionText = actionText;
    this.modelId = modelId;
    this.musicListId = musicListId;
    this.modelList = null;

    if (modelSrc === null && modelId === undefined) {
      throw new Error("ModelID and ModelSrc 为空, 必须填写其一.");
    }
    switch (modelId) {
      case 0:
      case 1:
      case 2:
      case 3:
        this.executor();
        break;
      default:
        throw new Error("ModelId 不符合规则 (id: 0 ~ 3)");
    }
  }
  executor() {
    this.loadWidget();
  }
  loadWidget() {
    if (this.modelSrc) {
      console.log(`location found ${this.modelSrc}`);
    } else {
      console.log("network to loading");
    }

    document.body.insertAdjacentHTML(
      "beforebegin",
      `
      <div id="live2d-widget">
        <div class="live2d-tips"></div>
        <canvas id="live2d" width="830" height="900"></canvas>
        <div class="live2d-tools">
          <span class="fa fa-lg fa-camera-retro"></span>
          <span class="fa fa-lg fa-music"></span>
          <span class="fa fa-lg fa-info-circle"></span>
          <span class="fa fa-lg fa-times"></span>
        </div>
      </div>
      <div class="live2d-toggle">看板娘</div>
    `
    );

    this.setSite();

    let userAction = false,
      textTimer,
      actionTimer;

    (function welcomeWidget() {
      const now = new Date().getHours();
      let text;

      if (location.pathname !== "/") {
        if (now >= 5 && now < 7) {
          text = `早上好,我的主人`;
        } else if (now >= 7 && now < 11) {
          text = `上午过的舒服吗?<span>不要太劳累哦。</span>`;
        } else if (now >= 11 && now < 13) {
          text = `该吃午饭了,休息休息吧~`;
        } else if (now >= 13 && now < 15) {
          text = `工作时间,喝杯茶<span>「提提神」</span>`;
        } else if (now >= 15 && now < 17) {
          text = `完成的差不多了,赶紧收工吧!`;
        } else if (now >= 17 && now < 19) {
          text = `吃晚饭咯,一天过得真快呀。`;
        } else if (now >= 19 && now < 21) {
          text = `晚上好,今天过得这么样?`;
        } else if (now >= 21 && now < 23) {
          text = `早点睡吧,对身体好,明天又是<span>「元气满满」</span>的一天`;
        } else {
          text = `快点睡觉!再不睡觉,我生气了哦。`;
        }
      }
      showMessage(text, 7000, 8);
    })();

    window.addEventListener("mousemove", () => (userAction = true));
    window.addEventListener("keydown", () => (userAction = true));

    setInterval(() => {
      if (userAction) {
        userAction = false;
        clearInterval(actionTimer);
        actionTimer = null;
      } else if (!actionTimer) {
        actionTimer = setInterval(() => {
          showMessage(this.actionText, 4000, 9);
        }, this.actionTime);
      }
    }, 2000);

    let musicArr = [],
      audio = null;

    document
      .querySelector("#live2d-widget #live2d")
      .addEventListener("click", () => {
        hitokoto("动画");
      });
    document
      .querySelector("#live2d-widget #live2d")
      .addEventListener("mouseover", () => {
        let tools = document.querySelector("#live2d-widget .live2d-tools");

        tools.classList.add("active");
      });
    document
      .querySelector("#live2d-widget .live2d-tools")
      .addEventListener("mouseover", function () {
        this.classList.add("active");
      });
    document
      .querySelector("#live2d-widget #live2d")
      .addEventListener("mouseout", () => {
        let tools = document.querySelector("#live2d-widget .live2d-tools");

        tools.classList.remove("active");
      });
    document
      .querySelector(".live2d-tools .fa-camera-retro")
      .addEventListener("click", () => {
        showMessage(`卡哇伊,合影留念吧~`, 6000, 9);
        Live2D.captureName = "photo.png";
        Live2D.captureFrame = true;
      });
    document
      .querySelector(".live2d-tools .fa-music")
      .addEventListener("click", () => {
        // 调用hitokoto提供的api
        fetch163Playlist(this.musicListId)
          .then((data) => {
            for (const music in data) {
              musicArr.push(data[music].url);
            }
            return musicArr;
          })
          .then((music) => {
            if (audio) {
              audio.load();
            }
            audio = new Audio(randSection(music));
            audio.play();
          })
          .catch(console.error);
      });
    document
      .querySelector(".live2d-tools .fa-info-circle")
      .addEventListener("click", () => {
        showMessage(`Go go go~`, 6000, 9);
        open("https://www.live2d.com/en/");
      });
    document
      .querySelector(".live2d-tools .fa-times")
      .addEventListener("click", function () {
        let live2d = document.querySelector("#live2d-widget");

        live2d.style.bottom = "-500px";
        setTimeout(() => {
          live2d.style.display = "none";
          document.querySelector(".live2d-toggle").classList.add("active");
        }, 2000);
      });
    document.querySelector(".live2d-toggle").addEventListener("click", () => {
      document.querySelector(".live2d-toggle").classList.remove("active");
      let live2d = document.querySelector("#live2d-widget");
      console.log(this.vOffset);
      live2d.style.display = "block";
      setTimeout(() => {
        live2d.style.bottom = this.vOffset + "px";
      }, 0);
    });

    function hitokoto(typeName) {
      let type;
      switch (typeName) {
        case "动画":
          type = "a";
          break;
        case "漫画":
          type = "b";
          break;
        case "游戏":
          type = "c";
          break;
        default:
          typeName = "动画";
          type = "a";
          break;
      }
      fetch(`https://v1.hitokoto.cn/?c=${type}`)
        .then((response) => response.json())
        .then((result) => {
          const text = `来自${typeName} <span>「${result.from}」</span> 的留言`;
          showMessage(result.hitokoto, 4000, 9);
          setTimeout(() => {
            showMessage(text, 4000, 9);
          }, 4000);
        });
    }

    const loadJSON = async (CDN) => {
      const response = await fetch(`${CDN}model_list.json`);
      this.modelList = await response.json();
    };

    const loadModel = async (
      CDN = "https://cdn.jsdelivr.net/gh/fghrsh/live2d_api/"
    ) => {
      if (!this.modelSrc) {
        await loadJSON(CDN);
        let target = this.modelList.models[this.modelId];
        loadlive2d("live2d", `${CDN}model/${target}/index.json`);
      } else {
        loadlive2d("live2d", this.modelSrc);
      }
    };

    function randSection(obj) {
      return Array.isArray(obj)
        ? obj[Math.floor(Math.random() * obj.length)]
        : obj;
    }

    function showMessage(text, timeout, protery) {
      if (
        !text ||
        (sessionStorage.getItem("tips-text") &&
          sessionStorage.getItem("tips-text") > protery)
      )
        return;

      if (textTimer) {
        clearTimeout(textTimer);
        textTimer = null;
      }
      sessionStorage.setItem("tips-text", protery);
      let tips = document.querySelector("#live2d-widget .live2d-tips");
      let tip = randSection(text);
      tips.innerHTML = tip;
      tips.classList.add("active");

      textTimer = setTimeout(() => {
        tips.classList.remove("active");
        sessionStorage.removeItem("tips-text");
      }, timeout);
    }

    const devtools = () => {};
    console.log("%c", devtools);
    devtools.toString = () => {
      showMessage(
        `Live2d 有官网文档哦 ~ 请访问 <span>「https://www.live2d.com/en/」</span>`,
        4000,
        9
      );
    };
    window.addEventListener("copy", () => {
      showMessage(`你想获得力量吗?`, 4000, 9);
    });
    window.addEventListener("visibilitychange", () => {
      if (!document.hidden) showMessage("你还好吗,担心死你了~", 4000, 9);
    });

    loadModel();
  }
  setSite() {
    let live2d = document.querySelector("#live2d-widget");
    setTimeout(() => {
      live2d.style.bottom = this.vOffset + "px";
      live2d.style.left = this.hOffset + "px";
      live2d.style.width = this.width + "px";
      live2d.style.height = this.height + "px";
    }, 0);
  }
}

l o a d . j s load.js load.js(加载标签)

/** 异步加载标签类 */
export default class Autoload {
  constructor() {
    return Promise.all([
      this.loadExtendResource("./live2d.min.js", "js"),
      this.loadExtendResource("./live2d.music.js", "js"),
      this.loadExtendResource("./live2d.tips.js", "js"),
      this.loadExtendResource("./demo.css", "css"),
    ]);
  }
  loadExtendResource(url, type) {
    return new Promise((resolve, reject) => {
      let tag;

      switch (type) {
        case "css":
          tag = document.createElement("link");
          tag.href = url;
          tag.rel = "stylesheet";
          break;
        case "js":
          tag = document.createElement("script");
          tag.src = url;
          break;
      }

      if (tag) {
        tag.onload = () => resolve(url);
        tag.onerror = () => reject(url);
        document.head.appendChild(tag);
      }
    });
  }
}

l i v e 2 d . m u s i c . j s live2d.music.js live2d.music.js(获取音乐)

/** 获取歌单数据,一言官网提供的示例 https://developer.hitokoto.cn/sentence/demo */
// 获取歌单列表数据
function fetch163Playlist(playlistId) {
  return new Promise((ok, err) => {
    fetch(`https://v1.hitokoto.cn/nm/playlist/${playlistId}`)
      .then((response) => response.json())
      .then((data) => {
        const arr = [];
        data.playlist.tracks.map(function (value) {
          arr.push(value.id);
        });
        return arr;
      })
      .then(fetch163Songs)
      .then(ok)
      .catch(err);
  });
}
// 获取歌曲数据
function fetch163Songs(Ids) {
  return new Promise(function (ok, err) {
    let ids;
    switch (typeof Ids) {
      case "number":
        ids = [Ids];
        break;
      case "object":
        if (!Array.isArray(Ids)) {
          err(new Error("Please enter array or number"));
          return;
        }
        ids = Ids;
        break;
      default:
        err(new Error("Please enter array or number"));
        return;
        break;
    }
    fetch(
      `https://v1.hitokoto.cn/nm/summary/${ids.join(
        ","
      )}?lyric=true&common=true`
    )
      .then((response) => response.json())
      .then((data) => {
        var songs = [];
        data.songs.map(function (song) {
          songs.push({
            name: song.name,
            url: song.url,
            artist: song.artists.join("/"),
            album: song.album.name,
            pic: song.album.picture,
            lrc: song.lyric,
          });
        });
        return songs;
      })
      .then(ok)
      .catch(err);
  });
}

d e m o . c s s demo.css demo.css(看板娘样式表)

/** demo.css */
div#live2d-widget {
  position: fixed;
  transition: 3s;
}

div#live2d-widget canvas#live2d {
  width: 300px;
  height: 300px;
}

div.live2d-toggle {
  margin-left: -100px;
  position: fixed;
  bottom: 66px;
  background: #fa0;
  border-radius: 5px;
  color: #fff;
  font-size: 12px;
  padding: 5px;
  transition: 1s;
  width: 60px;
  writing-mode: vertical-rl;
}

div.live2d-toggle.active {
  margin-left: -50px;
}
div.live2d-toggle.active:hover {
  margin-left: -30px;
}

div#live2d-widget div.live2d-tips {
  position: absolute;
  width: 250px;
  height: 100px;
  background: #ffbcd8;
  border-radius: 5px;
  border: 1px solid #ff8cc8;
  color: #000;
  filter: blur(5px);
  box-sizing: border-box;
  padding: 10px;
  left: 50%;
  top: -80px;
  transform: translateX(-50%);
  animation: tipsAnim 25s ease-in-out 5s infinite;
  transition: opacity 1s;
  opacity: 0;
  box-shadow: 0 0 10px 3px rgba(255, 188, 216, 0.7);
  font-family: "黑体";
  font-size: 14px;
  z-index: -1;
}

div#live2d-widget div.live2d-tips.active {
  transition: opacity 0.2s;
  opacity: 1;
  filter: none;
}
div#live2d-widget div.live2d-tips > span {
  color: #29c5ff;
}
div#live2d-widget div.live2d-tools {
  position: absolute;
  bottom: 50%;
  transform: translate(0, 50%);
  right: 30px;
  opacity: 0;
  transition: opacity 1s;
}
div#live2d-widget div.live2d-tools.active {
  opacity: 1;
  transition: opacity 0.5s;
}

div#live2d-widget div.live2d-tools > span {
  line-height: 30px;
  display: block;
}
@keyframes tipsAnim {
  2% {
    transform: translateX(-50%) translate(0.5px, -1.5px) rotate(-0.5deg);
  }
  4% {
    transform: translateX(-50%) translate(0.5px, 1.5px) rotate(1.5deg);
  }
  6% {
    transform: translateX(-50%) translate(1.5px, 1.5px) rotate(1.5deg);
  }
  8% {
    transform: translateX(-50%) translate(2.5px, 1.5px) rotate(0.5deg);
  }
  10% {
    transform: translateX(-50%) translate(0.5px, 2.5px) rotate(0.5deg);
  }
  12% {
    transform: translateX(-50%) translate(1.5px, 1.5px) rotate(0.5deg);
  }
  14% {
    transform: translateX(-50%) translate(0.5px, 0.5px) rotate(0.5deg);
  }
  16% {
    transform: translateX(-50%) translate(-1.5px, -0.5px) rotate(1.5deg);
  }
  18% {
    transform: translateX(-50%) translate(0.5px, 0.5px) rotate(1.5deg);
  }
  20% {
    transform: translateX(-50%) translate(2.5px, 2.5px) rotate(1.5deg);
  }
  22% {
    transform: translateX(-50%) translate(0.5px, -1.5px) rotate(1.5deg);
  }
  24% {
    transform: translateX(-50%) translate(-1.5px, 1.5px) rotate(-0.5deg);
  }
  26% {
    transform: translateX(-50%) translate(1.5px, 0.5px) rotate(1.5deg);
  }
  28% {
    transform: translateX(-50%) translate(-0.5px, -0.5px) rotate(-0.5deg);
  }
  30% {
    transform: translateX(-50%) translate(1.5px, -0.5px) rotate(-0.5deg);
  }
  32% {
    transform: translateX(-50%) translate(2.5px, -1.5px) rotate(1.5deg);
  }
  34% {
    transform: translateX(-50%) translate(2.5px, 2.5px) rotate(-0.5deg);
  }
  36% {
    transform: translateX(-50%) translate(0.5px, -1.5px) rotate(0.5deg);
  }
  38% {
    transform: translateX(-50%) translate(2.5px, -0.5px) rotate(-0.5deg);
  }
  40% {
    transform: translateX(-50%) translate(-0.5px, 2.5px) rotate(0.5deg);
  }
  42% {
    transform: translateX(-50%) translate(-1.5px, 2.5px) rotate(0.5deg);
  }
  44% {
    transform: translateX(-50%) translate(-1.5px, 1.5px) rotate(0.5deg);
  }
  46% {
    transform: translateX(-50%) translate(1.5px, -0.5px) rotate(-0.5deg);
  }
  48% {
    transform: translateX(-50%) translate(2.5px, -0.5px) rotate(0.5deg);
  }
  50% {
    transform: translateX(-50%) translate(-1.5px, 1.5px) rotate(0.5deg);
  }
  52% {
    transform: translateX(-50%) translate(-0.5px, 1.5px) rotate(0.5deg);
  }
  54% {
    transform: translateX(-50%) translate(-1.5px, 1.5px) rotate(0.5deg);
  }
  56% {
    transform: translateX(-50%) translate(0.5px, 2.5px) rotate(1.5deg);
  }
  58% {
    transform: translateX(-50%) translate(2.5px, 2.5px) rotate(0.5deg);
  }
  60% {
    transform: translateX(-50%) translate(2.5px, -1.5px) rotate(1.5deg);
  }
  62% {
    transform: translateX(-50%) translate(-1.5px, 0.5px) rotate(1.5deg);
  }
  64% {
    transform: translateX(-50%) translate(-1.5px, 1.5px) rotate(1.5deg);
  }
  66% {
    transform: translateX(-50%) translate(0.5px, 2.5px) rotate(1.5deg);
  }
  68% {
    transform: translateX(-50%) translate(2.5px, -1.5px) rotate(1.5deg);
  }
  70% {
    transform: translateX(-50%) translate(2.5px, 2.5px) rotate(0.5deg);
  }
  72% {
    transform: translateX(-50%) translate(-0.5px, -1.5px) rotate(1.5deg);
  }
  74% {
    transform: translateX(-50%) translate(-1.5px, 2.5px) rotate(1.5deg);
  }
  76% {
    transform: translateX(-50%) translate(-1.5px, 2.5px) rotate(1.5deg);
  }
  78% {
    transform: translateX(-50%) translate(-1.5px, 2.5px) rotate(0.5deg);
  }
  80% {
    transform: translateX(-50%) translate(-1.5px, 0.5px) rotate(-0.5deg);
  }
  82% {
    transform: translateX(-50%) translate(-1.5px, 0.5px) rotate(-0.5deg);
  }
  84% {
    transform: translateX(-50%) translate(-0.5px, 0.5px) rotate(1.5deg);
  }
  86% {
    transform: translateX(-50%) translate(2.5px, 1.5px) rotate(0.5deg);
  }
  88% {
    transform: translateX(-50%) translate(-1.5px, 0.5px) rotate(1.5deg);
  }
  90% {
    transform: translateX(-50%) translate(-1.5px, -0.5px) rotate(-0.5deg);
  }
  92% {
    transform: translateX(-50%) translate(-1.5px, -1.5px) rotate(1.5deg);
  }
  94% {
    transform: translateX(-50%) translate(0.5px, 0.5px) rotate(-0.5deg);
  }
  96% {
    transform: translateX(-50%) translate(2.5px, -0.5px) rotate(-0.5deg);
  }
  98% {
    transform: translateX(-50%) translate(-1.5px, -1.5px) rotate(-0.5deg);
  }
  0%,
  100% {
    transform: translateX(-50%) translate(0, 0) rotate(0);
  }
}

d e m o . h t m l demo.html demo.html(用于测试的文档)

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <link
      rel="stylesheet"
      href="https://cdn.jsdelivr.net/npm/font-awesome/css/font-awesome.min.css"
    />
    <title>My test</title>
  </head>
  <body>
    <script type="module">
      import Autoload from "./load.js";
      new Autoload().then((value) => {
        new Live2d_tips({
          modelId: 2,
          modelSrc: null,
          modelSite: {
            width: 300,
            height: 300,
            hOffset: 0,
            vOffset: 0,
          },
          musicListId: 6752075988,
          actionTime: 5000,
          actionText: ["你为啥不理我了", "又是颓废的一天", "Live2d 启动!"],
        });
      });
    </script>
  </body>
</html>
南无阿弥陀佛
上一篇:如何在程序中给word文档加上标和下标


下一篇:Flutter Widget -Text