feat(server): finish server part (1/2)
This commit is contained in:
13
README.md
13
README.md
@@ -6,14 +6,13 @@ The `LICENSE` file applies to all files unless listed specifically.
|
||||
|
||||
`LICENSE_SPINE` file applies to following files including adapted code for this repo:
|
||||
|
||||
- `template/build/spine-widget.js`
|
||||
- `template/new/build/spine-player.css`
|
||||
- `template/new/build/spine-player.js`
|
||||
- `release/*/build/spine-player.css`
|
||||
- `release/*/build/spine-player.js`
|
||||
- `template/assets/spine-player.css`
|
||||
- `template/assets/spine-player.js`
|
||||
- `release/*/assets/spine-player.css`
|
||||
- `release/*/assets/spine-player.js`
|
||||
|
||||
`Copyright © 2017 - 2021 Arknights/Hypergryph Co., Ltd` applies to following files:
|
||||
|
||||
- all files under `operator` folder and its sub-folders
|
||||
- all files under `release/*/assets/*` folder
|
||||
- `release/*/build/operator.js`
|
||||
- all files under `release/*/operator/*` folder
|
||||
- `release/*/operator/operator.js`
|
||||
95
aklive2d.py
Normal file
95
aklive2d.py
Normal file
@@ -0,0 +1,95 @@
|
||||
import argparse
|
||||
import sys
|
||||
|
||||
from lib.config import Config
|
||||
from lib.server import Server
|
||||
from lib.builder import Builder
|
||||
|
||||
class AkLive2D:
|
||||
|
||||
def __init__(self) -> None:
|
||||
self.config = Config().config
|
||||
self.args = None
|
||||
self.running = None
|
||||
|
||||
def start(self):
|
||||
parser = argparse.ArgumentParser(
|
||||
prog="aklive2d",
|
||||
description="Arknights Live 2D Wallpaper Builder",
|
||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter
|
||||
)
|
||||
parser.add_argument(
|
||||
"-d",
|
||||
"--debug",
|
||||
dest="debug",
|
||||
action="store_true",
|
||||
help="To debug the app"
|
||||
)
|
||||
|
||||
subprasers = parser.add_subparsers(
|
||||
title="Available commands",
|
||||
dest="command",
|
||||
required=True,
|
||||
help="<Required> Select the command to run"
|
||||
)
|
||||
|
||||
server = subprasers.add_parser(
|
||||
"server",
|
||||
help="Development Server",
|
||||
aliases=['s'],
|
||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter
|
||||
)
|
||||
server.add_argument(
|
||||
"-p",
|
||||
"--port",
|
||||
dest="port",
|
||||
type=int,
|
||||
default=8080,
|
||||
help="Development server port"
|
||||
)
|
||||
server.add_argument(
|
||||
"-o",
|
||||
"--operator",
|
||||
dest="operator_name",
|
||||
type=str,
|
||||
required=True,
|
||||
help="<Required> Operatro to develop"
|
||||
)
|
||||
|
||||
build = subprasers.add_parser(
|
||||
"build",
|
||||
help="Build releases",
|
||||
aliases=['b'],
|
||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter
|
||||
)
|
||||
build.add_argument(
|
||||
"-o",
|
||||
"--operator",
|
||||
dest="operator_names",
|
||||
type=str,
|
||||
default="all",
|
||||
nargs='+',
|
||||
help="Operatro to build",
|
||||
choices=["all"] + [key for key in self.config["operators"]]
|
||||
)
|
||||
|
||||
self.args = parser.parse_args()
|
||||
if self.args.command == "server" or self.args.command == "s":
|
||||
self.running = Server(self.args.port, self.args.operator_name, self.config)
|
||||
|
||||
if self.args.command == "build" or self.args.command == "b":
|
||||
self.running = Builder(self.args.operator_names, self.config["operators"])
|
||||
|
||||
self.running.start()
|
||||
|
||||
def stop(self):
|
||||
self.running.stop()
|
||||
return
|
||||
|
||||
if __name__ == "__main__":
|
||||
aklive2d = AkLive2D()
|
||||
try:
|
||||
aklive2d.start()
|
||||
except KeyboardInterrupt:
|
||||
print("\nInterrupted, exiting...")
|
||||
sys.exit()
|
||||
12
config.yaml
12
config.yaml
@@ -1,12 +1,10 @@
|
||||
operators:
|
||||
skadi:
|
||||
common_name: dyn_illust_char_1012_skadi2
|
||||
source_folder: ./operator/skadi/extracted/
|
||||
target_folder: ./operator/skadi/
|
||||
release_folder: ./release/{name}/
|
||||
source_folder: ./operator/{name}/extracted/
|
||||
target_folder: ./operator/{name}/
|
||||
server:
|
||||
asset_access_folder: /asset/
|
||||
css_access_folder: /build/
|
||||
js_access_folder: /build/
|
||||
operator_folder: /operator/
|
||||
release_folder: ./release/
|
||||
template_folder: ./template/
|
||||
version_file: ./VERSION
|
||||
template_folder: /template/
|
||||
|
||||
44
lib/base64_util.py
Normal file
44
lib/base64_util.py
Normal file
@@ -0,0 +1,44 @@
|
||||
import base64
|
||||
import pathlib
|
||||
|
||||
def encode_image(path, prefix=True):
|
||||
with open(pathlib.Path.cwd().joinpath(path), "rb") as f:
|
||||
bytes = f.read()
|
||||
encoded_bytes = base64.b64encode(bytes)
|
||||
humanreadable_data = encoded_bytes.decode("utf-8")
|
||||
if prefix is True:
|
||||
result = "data:image/png;base64," + humanreadable_data
|
||||
else:
|
||||
result = humanreadable_data
|
||||
return result
|
||||
|
||||
def decode_image(data: str, path):
|
||||
if data.strip().startswith("data:") is True:
|
||||
data = data.split(",")[1]
|
||||
encoded_bytes = data.encode("utf-8")
|
||||
with open(pathlib.Path.cwd().joinpath(path), "wb") as f:
|
||||
bytes = base64.decodebytes(encoded_bytes)
|
||||
f.write(bytes)
|
||||
|
||||
def encode_string(data=None, type="text/plain", path=None, prefix=True, encoding="utf-8"):
|
||||
if data is None and path is None:
|
||||
return
|
||||
if data is not None:
|
||||
bytes = data.encode(encoding)
|
||||
elif path is not None:
|
||||
with open(pathlib.Path.cwd().joinpath(path), "r") as f:
|
||||
bytes = f.read()
|
||||
encoded_bytes = base64.b64encode(bytes)
|
||||
humanreadable_data = encoded_bytes.decode(encoding)
|
||||
if prefix is True:
|
||||
result = "data:{};base64,".format(type) + humanreadable_data
|
||||
else:
|
||||
result = humanreadable_data
|
||||
return result
|
||||
|
||||
def decode_string(data:str, encoding="utf-8"):
|
||||
if data.strip().startswith("data:") is True:
|
||||
data = data.split(",")[1]
|
||||
encoded_bytes = data.encode(encoding)
|
||||
bytes = base64.decodebytes(encoded_bytes)
|
||||
return bytes.decode(encoding)
|
||||
135
lib/builder.py
135
lib/builder.py
@@ -1,46 +1,153 @@
|
||||
import threading
|
||||
|
||||
import shutil
|
||||
from lib.skeleton_binary import SkeletonBinary
|
||||
from lib.alpha_composite import AlphaComposite
|
||||
from lib.atlas_reader import AtlasReader
|
||||
from lib.base64_util import *
|
||||
|
||||
class Builder:
|
||||
|
||||
def __init__(self, operator_name, config) -> None:
|
||||
self.operator_name = operator_name
|
||||
self.config = config
|
||||
self.source_path = config["operators"][operator_name]["source_folder"].format(name=operator_name)
|
||||
self.target_path = config["operators"][operator_name]["target_folder"].format(name=operator_name)
|
||||
self.common_name = config["operators"][operator_name]["common_name"]
|
||||
self.file_paths = dict(
|
||||
json=self.target_path + self.common_name + ".json",
|
||||
atlas=self.target_path + self.common_name + ".atlas",
|
||||
skel=self.target_path + self.common_name + ".skel",
|
||||
)
|
||||
|
||||
def start(self):
|
||||
self.__set_version()
|
||||
# self.__build_assets()
|
||||
# self.__set_version()
|
||||
# name = "skadi"
|
||||
# print(self.config[name]["source_folder"].format(name=name))
|
||||
source = "./operator/skadi/extracted/"
|
||||
target = "./operator/skadi/"
|
||||
name = "dyn_illust_char_1012_skadi2"
|
||||
print("build")
|
||||
return
|
||||
|
||||
def stop(self):
|
||||
return
|
||||
|
||||
def __build_assets(self):
|
||||
source = "./operator/skadi/extracted/"
|
||||
target = "./operator/skadi/"
|
||||
name = "dyn_illust_char_1012_skadi2"
|
||||
ar = AtlasReader(source + name, target + name)
|
||||
skeleton_binary = threading.Thread(
|
||||
def build_assets(self):
|
||||
operator_file = pathlib.Path.cwd().joinpath(self.target_path, "operator.js")
|
||||
if operator_file.exists() is False:
|
||||
print("Building operaotr data...")
|
||||
|
||||
alpha_composite_threads = list()
|
||||
png_to_base64_threads = list()
|
||||
prefix = "window.operator = "
|
||||
data = dict()
|
||||
|
||||
ar = AtlasReader(self.source_path + self.common_name, self.target_path + self.common_name)
|
||||
|
||||
skeleton_binary_thread = threading.Thread(
|
||||
target=SkeletonBinary,
|
||||
args=(source + name, target + name)
|
||||
args=(self.source_path + self.common_name, self.target_path + self.common_name),
|
||||
daemon=True,
|
||||
)
|
||||
ar_thread = threading.Thread(
|
||||
target=ar.get_images,
|
||||
args=()
|
||||
daemon=True,
|
||||
)
|
||||
atlas_base64_thread = threading.Thread(
|
||||
target=self.__atlas_to_base64,
|
||||
args=(
|
||||
self.file_paths["atlas"],
|
||||
data,
|
||||
".{}".format(self.config["server"]["operator_folder"]) + self.common_name + ".atlas",
|
||||
),
|
||||
daemon=True,
|
||||
)
|
||||
|
||||
ar_thread.start()
|
||||
skeleton_binary.start()
|
||||
skeleton_binary_thread.start()
|
||||
ar_thread.join()
|
||||
atlas_base64_thread.start()
|
||||
|
||||
# alpha composite
|
||||
for item in ar.images:
|
||||
threading.Thread(
|
||||
alpha_composite_thread = threading.Thread(
|
||||
target=AlphaComposite,
|
||||
args=(source + item, target + item)
|
||||
).start()
|
||||
args=(self.source_path + item, self.target_path + item),
|
||||
daemon=True,
|
||||
)
|
||||
alpha_composite_threads.append(alpha_composite_thread)
|
||||
alpha_composite_thread.start()
|
||||
|
||||
for thread in alpha_composite_threads:
|
||||
thread.join()
|
||||
|
||||
for item in ar.images:
|
||||
png_to_base64_thread = threading.Thread(
|
||||
target=self.__png_to_base64,
|
||||
args=(
|
||||
self.target_path + item,
|
||||
data,
|
||||
".{}".format(self.config["server"]["operator_folder"]) + item,
|
||||
),
|
||||
daemon=True,
|
||||
)
|
||||
|
||||
png_to_base64_threads.append(png_to_base64_thread)
|
||||
png_to_base64_thread.start()
|
||||
|
||||
skeleton_binary_thread.join()
|
||||
|
||||
json_base64_thread =threading.Thread(
|
||||
target=self.__json_to_base64,
|
||||
args=(
|
||||
self.file_paths["json"],
|
||||
data,
|
||||
".{}".format(self.config["server"]["operator_folder"]) + self.common_name + ".json",
|
||||
),
|
||||
daemon=True,
|
||||
)
|
||||
json_base64_thread.start()
|
||||
json_base64_thread.join()
|
||||
|
||||
for thread in png_to_base64_threads:
|
||||
thread.join()
|
||||
|
||||
atlas_base64_thread.join()
|
||||
|
||||
jsonContent = prefix + str(data)
|
||||
with open(operator_file, "w") as f:
|
||||
f.write(jsonContent)
|
||||
|
||||
print("Finished building operaotr data")
|
||||
return
|
||||
|
||||
def __set_version(self):
|
||||
version = input("Enter build version: ")
|
||||
print(version)
|
||||
return
|
||||
|
||||
def __json_to_base64(self, path, dict=None, key=None):
|
||||
with open(pathlib.Path.cwd().joinpath(path), "r") as f:
|
||||
data = f.read()
|
||||
result = encode_string(data, type="application/json")
|
||||
if dict is not None and key is not None:
|
||||
dict[key] = result
|
||||
else:
|
||||
return result
|
||||
|
||||
def __atlas_to_base64(self, path, dict=None, key=None):
|
||||
with open(pathlib.Path.cwd().joinpath(path), "r") as f:
|
||||
data = f.read()
|
||||
result = encode_string(data, type="application/octet-stream")
|
||||
if dict is not None and key is not None:
|
||||
dict[key] = result
|
||||
else:
|
||||
return result
|
||||
|
||||
def __png_to_base64(self, path, dict=None, key=None):
|
||||
result = encode_image(path)
|
||||
if dict is not None and key is not None:
|
||||
dict[key] = result
|
||||
else:
|
||||
return result
|
||||
|
||||
@@ -4,6 +4,23 @@ class Config:
|
||||
|
||||
def __init__(self) -> None:
|
||||
self.config_path = pathlib.Path.cwd().joinpath("config.yaml")
|
||||
self.valid_keys = dict(
|
||||
config=dict(
|
||||
server=dict,
|
||||
operators=dict
|
||||
),
|
||||
server=dict(
|
||||
template_folder=str,
|
||||
release_folder=str,
|
||||
operator_folder=str
|
||||
),
|
||||
operators=dict(
|
||||
source_folder=str,
|
||||
target_folder=str,
|
||||
common_name=str,
|
||||
release_folder=str
|
||||
)
|
||||
)
|
||||
self.__read_config()
|
||||
|
||||
def __read_config(self):
|
||||
@@ -15,29 +32,15 @@ class Config:
|
||||
self.__validate_config()
|
||||
|
||||
def __validate_config(self):
|
||||
config_keys = dict(
|
||||
server=dict,
|
||||
operators=dict
|
||||
)
|
||||
self.__config_check("config", self.config, config_keys)
|
||||
key = "config"
|
||||
self.__config_check(key, self.config, self.valid_keys[key])
|
||||
|
||||
server_keys = dict(
|
||||
template_folder=str,
|
||||
release_folder=str,
|
||||
js_access_folder=str,
|
||||
css_access_folder=str,
|
||||
asset_access_folder=str,
|
||||
version_file=str
|
||||
)
|
||||
self.__config_check("server", self.config["server"], server_keys)
|
||||
key = "server"
|
||||
self.__config_check(key, self.config[key], self.valid_keys[key])
|
||||
|
||||
operators_keys = dict(
|
||||
source_folder=str,
|
||||
target_folder=str,
|
||||
common_name=str
|
||||
)
|
||||
for operator_name, operator_content in self.config["operators"].items():
|
||||
self.__config_check(operator_name, operator_content, operators_keys)
|
||||
key = "operators"
|
||||
for operator_name, operator_content in self.config[key].items():
|
||||
self.__config_check(operator_name, operator_content, self.valid_keys[key])
|
||||
|
||||
with open(self.config_path, 'w') as f:
|
||||
yaml.safe_dump(self.config, f, allow_unicode=True)
|
||||
|
||||
@@ -1,12 +1,47 @@
|
||||
from http.server import SimpleHTTPRequestHandler
|
||||
import pathlib
|
||||
from socketserver import TCPServer
|
||||
|
||||
from lib.builder import Builder
|
||||
class Server:
|
||||
|
||||
def __init__(self, port, config) -> None:
|
||||
self.port = port
|
||||
def __init__(self, port, operator, config) -> None:
|
||||
self.config = config
|
||||
self.operator = operator
|
||||
self.port = port
|
||||
self.httpd = TCPServer(("", port), httpd(operator, config["server"], directory=str(pathlib.Path.cwd())))
|
||||
|
||||
def start(self):
|
||||
# build assets first
|
||||
Builder(self.operator, self.config).build_assets()
|
||||
print("Server is up at 0.0.0.0:{port}".format(port=self.port))
|
||||
self.httpd.serve_forever()
|
||||
return
|
||||
|
||||
def stop(self):
|
||||
self.httpd.server_close()
|
||||
return
|
||||
|
||||
class httpd(SimpleHTTPRequestHandler):
|
||||
|
||||
def __init__(self, operator, config, directory):
|
||||
self.config = config
|
||||
self.operator = operator
|
||||
self.template_path = directory
|
||||
|
||||
def __call__(self, *args, **kwds):
|
||||
super().__init__(*args, directory=self.template_path, **kwds)
|
||||
|
||||
def do_GET(self):
|
||||
split_path = self.path.split("/")
|
||||
access_path = "/{}/".format(split_path[1])
|
||||
|
||||
if self.path == "/":
|
||||
self.path = self.config["template_folder"] + "index.html"
|
||||
elif access_path == "/assets/":
|
||||
# assets folder
|
||||
self.path = self.config["template_folder"] + "assets/" + "/".join([i for i in split_path[2:]])
|
||||
elif self.config["operator_folder"] == access_path:
|
||||
# operator folder
|
||||
self.path = self.config["operator_folder"] + "{}/".format(self.operator) + split_path[-1]
|
||||
return SimpleHTTPRequestHandler.do_GET(self)
|
||||
@@ -62,7 +62,7 @@ CURVE_BEZIER = 2
|
||||
|
||||
class SkeletonBinary:
|
||||
|
||||
def __init__(self, file_path, save_path, scale=1) -> None:
|
||||
def __init__(self, file_path, save_path, scale=1):
|
||||
if file_path.strip().endswith(".skel") is False:
|
||||
file_path += ".skel"
|
||||
if save_path.strip().endswith(".json") is False:
|
||||
@@ -78,7 +78,7 @@ class SkeletonBinary:
|
||||
self.readSkeletonData()
|
||||
|
||||
with open(pathlib.Path.cwd().joinpath(save_path), "w") as f:
|
||||
json.dump(self.dict, f, indent=2, sort_keys=True)
|
||||
json.dump(self.dict, f)
|
||||
except Exception as e:
|
||||
print(e)
|
||||
|
||||
@@ -743,10 +743,12 @@ class SkeletonBinary:
|
||||
if len(chars) < byteCount:
|
||||
chars = [None] * byteCount
|
||||
charCount = 0
|
||||
for i in range(byteCount):
|
||||
i = 0
|
||||
while (i < byteCount):
|
||||
b = self.read()
|
||||
shiftedB = b >> 4
|
||||
if shiftedB == -1:
|
||||
# 0b11110000 -> 0b11111111 ?
|
||||
raise ValueError("shiftedB is -1")
|
||||
elif shiftedB == 12 or shiftedB == 13:
|
||||
chars[charCount] = chr((b & 0x1F) << 6 | self.read() & 0x3F)
|
||||
|
||||
40
run.py
40
run.py
@@ -1,40 +0,0 @@
|
||||
import argparse
|
||||
|
||||
from lib.config import Config
|
||||
from lib.server import Server
|
||||
from lib.builder import Builder
|
||||
|
||||
class AkLive2D:
|
||||
|
||||
def __init__(self) -> None:
|
||||
self.config = Config().config
|
||||
self.args = None
|
||||
self.server = None
|
||||
self.builder = None
|
||||
|
||||
def start(self):
|
||||
parser = argparse.ArgumentParser(description="Arknights Live 2D Wallpaper Builder")
|
||||
arg_group = parser.add_mutually_exclusive_group(required=True)
|
||||
arg_group.add_argument("-s", "--serve", dest="port", type=int, const=8080,nargs="?", help="Development server port (default: 8080)")
|
||||
arg_group.add_argument("-b", "--build", dest="operator_name", type=str, const="all", nargs="?", help="Build wallpapers (default: all)")
|
||||
|
||||
self.args = parser.parse_args()
|
||||
|
||||
if self.args.port is not None:
|
||||
self.server = Server(self.args.port, self.config["server"])
|
||||
self.server.start()
|
||||
|
||||
if self.args.operator_name is not None:
|
||||
self.builder = Builder(self.args.operator_name, self.config["operators"])
|
||||
self.builder.start()
|
||||
|
||||
def stop(self):
|
||||
return
|
||||
|
||||
if __name__ == "__main__":
|
||||
aklive2d = AkLive2D()
|
||||
try:
|
||||
aklive2d.start()
|
||||
except KeyboardInterrupt:
|
||||
print("\nInterrupted, exiting...")
|
||||
aklive2d.stop()
|
||||
89
template/assets/runner.js
Normal file
89
template/assets/runner.js
Normal file
@@ -0,0 +1,89 @@
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
const RATIO = 0.618;
|
||||
var fps = 60;
|
||||
|
||||
if (params.has("fps")) {
|
||||
var tmp = parseInt(params.get("fps"));
|
||||
if (!isNaN(tmp)) {
|
||||
fps = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
function supportsWebGL() {
|
||||
try {
|
||||
let canvas = document.createElement("canvas");
|
||||
let ctx = canvas.getContext("webgl") || canvas.getContext("experimental-webgl");
|
||||
return ctx != null;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function calculateScale(width, height) {
|
||||
let windowWidth = window.innerWidth;
|
||||
let windowHeight = window.innerHeight;
|
||||
let scaleX = windowWidth / width;
|
||||
let scaleY = windowHeight / height;
|
||||
return { x: scaleX, y: scaleY };
|
||||
};
|
||||
|
||||
if (!supportsWebGL()) {
|
||||
alert('WebGL is unavailable. Fallback image will be used.');
|
||||
var e = document.getElementById("container");
|
||||
e.classList.add("fallback");
|
||||
e.parentElement.classList.add("widget-wrapper");
|
||||
|
||||
function fallback() {
|
||||
const scale = calculateScale(window.settings.fallbackImage.width, window.settings.fallbackImage.height);
|
||||
if (scale.x > scale.y) {
|
||||
e.style.width = window.settings.fallbackImage.width * scale.y + "px";
|
||||
e.style.height = window.settings.fallbackImage.height * scale.y + "px";
|
||||
} else if (scale.x < scale.y) {
|
||||
e.style.width = window.settings.fallbackImage.width * scale.x + "px";
|
||||
e.style.height = window.settings.fallbackImage.height * scale.x + "px";
|
||||
}
|
||||
}
|
||||
|
||||
window.addEventListener('resize', fallback, true);
|
||||
fallback();
|
||||
} else {
|
||||
var e = document.getElementById("container");
|
||||
var resetTime = window.performance.now();
|
||||
|
||||
var spinePlayer = new spine.SpinePlayer(e, {
|
||||
jsonUrl: window.settings.jsonUrl,
|
||||
atlasUrl: window.settings.atlasUrl,
|
||||
animation: window.settings.animation,
|
||||
rawDataURIs: window.operator,
|
||||
premultipliedAlpha: true,
|
||||
alpha: true,
|
||||
backgroundColor: "#00000000",
|
||||
fps: fps,
|
||||
viewport: window.settings.viewport,
|
||||
showControls: false,
|
||||
defaultMix: window.settings.defaultMix,
|
||||
success: window.settings.success,
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
|
||||
function resizeLogo() {
|
||||
document.getElementById("logo").width = window.innerWidth / 2 * RATIO
|
||||
}
|
||||
window.addEventListener('resize', resizeLogo, true);
|
||||
resizeLogo()
|
||||
|
||||
window.addEventListener("contextmenu", e => e.preventDefault());
|
||||
document.addEventListener("gesturestart", e => e.preventDefault());
|
||||
|
||||
// wallpaper engine
|
||||
window.wallpaperPropertyListener = {
|
||||
applyGeneralProperties: function (properties) {
|
||||
if (properties.fps) {
|
||||
spinePlayer.setFps(properties.fps);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
console.log("All resources are extracted from Arknights.")
|
||||
@@ -2089,11 +2089,15 @@ var spine;
|
||||
this.errors = {};
|
||||
this.toLoad = 0;
|
||||
this.loaded = 0;
|
||||
this.rawDataUris = {};
|
||||
this.textureLoader = textureLoader;
|
||||
this.pathPrefix = pathPrefix;
|
||||
}
|
||||
AssetManager.downloadText = function (url, success, error) {
|
||||
AssetManager.prototype.downloadText = function (url, success, error) {
|
||||
var request = new XMLHttpRequest();
|
||||
request.overrideMimeType("text/html");
|
||||
if (this.rawDataUris[url])
|
||||
url = this.rawDataUris[url];
|
||||
request.open("GET", url, true);
|
||||
request.onload = function () {
|
||||
if (request.status == 200) {
|
||||
@@ -2108,8 +2112,10 @@ var spine;
|
||||
};
|
||||
request.send();
|
||||
};
|
||||
AssetManager.downloadBinary = function (url, success, error) {
|
||||
AssetManager.prototype.downloadBinary = function (url, success, error) {
|
||||
var request = new XMLHttpRequest();
|
||||
if (this.rawDataUris[url])
|
||||
url = this.rawDataUris[url];
|
||||
request.open("GET", url, true);
|
||||
request.responseType = "arraybuffer";
|
||||
request.onload = function () {
|
||||
@@ -2125,13 +2131,16 @@ var spine;
|
||||
};
|
||||
request.send();
|
||||
};
|
||||
AssetManager.prototype.setRawDataURI = function (path, data) {
|
||||
this.rawDataUris[this.pathPrefix + path] = data;
|
||||
};
|
||||
AssetManager.prototype.loadText = function (path, success, error) {
|
||||
var _this = this;
|
||||
if (success === void 0) { success = null; }
|
||||
if (error === void 0) { error = null; }
|
||||
path = this.pathPrefix + path;
|
||||
this.toLoad++;
|
||||
AssetManager.downloadText(path, function (data) {
|
||||
this.downloadText(path, function (data) {
|
||||
_this.assets[path] = data;
|
||||
if (success)
|
||||
success(path, data);
|
||||
@@ -2150,12 +2159,13 @@ var spine;
|
||||
if (success === void 0) { success = null; }
|
||||
if (error === void 0) { error = null; }
|
||||
path = this.pathPrefix + path;
|
||||
var storagePath = path;
|
||||
this.toLoad++;
|
||||
var img = new Image();
|
||||
img.crossOrigin = "anonymous";
|
||||
img.onload = function (ev) {
|
||||
var texture = _this.textureLoader(img);
|
||||
_this.assets[path] = texture;
|
||||
_this.assets[storagePath] = texture;
|
||||
_this.toLoad--;
|
||||
_this.loaded++;
|
||||
if (success)
|
||||
@@ -2168,6 +2178,8 @@ var spine;
|
||||
if (error)
|
||||
error(path, "Couldn't load image " + path);
|
||||
};
|
||||
if (this.rawDataUris[path])
|
||||
path = this.rawDataUris[path];
|
||||
img.src = path;
|
||||
};
|
||||
AssetManager.prototype.loadTextureData = function (path, data, success, error) {
|
||||
@@ -2201,7 +2213,7 @@ var spine;
|
||||
var parent = path.lastIndexOf("/") >= 0 ? path.substring(0, path.lastIndexOf("/")) : "";
|
||||
path = this.pathPrefix + path;
|
||||
this.toLoad++;
|
||||
AssetManager.downloadText(path, function (atlasData) {
|
||||
this.downloadText(path, function (atlasData) {
|
||||
var pagesLoaded = { count: 0 };
|
||||
var atlasPages = new Array();
|
||||
try {
|
||||
@@ -9726,6 +9738,12 @@ var spine;
|
||||
return dom;
|
||||
}
|
||||
this.assetManager = new spine.webgl.AssetManager(this.context);
|
||||
if (config.rawDataURIs) {
|
||||
for (var path in config.rawDataURIs) {
|
||||
var data = config.rawDataURIs[path];
|
||||
this.assetManager.setRawDataURI(path, data);
|
||||
}
|
||||
}
|
||||
this.assetManager.loadText(config.jsonUrl);
|
||||
this.assetManager.loadTextureAtlas(config.atlasUrl);
|
||||
if (config.backgroundImage && config.backgroundImage.url)
|
||||
43
template/assets/style.css
Normal file
43
template/assets/style.css
Normal file
@@ -0,0 +1,43 @@
|
||||
html {
|
||||
-ms-user-select: none;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
user-select: none;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
background-size: cover;
|
||||
background-image: url("../operator/operator_bg.png");
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.logo {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 0;
|
||||
filter: invert(1);
|
||||
opacity: 0.3;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
.widget-wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.fallback {
|
||||
margin: auto;
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
background-size: cover;
|
||||
background-image: url("../operator/fallback.png");
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
@@ -1,148 +0,0 @@
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
const RATIO = 0.618;
|
||||
var fps = 60;
|
||||
|
||||
if (params.has("fps")) {
|
||||
var tmp = parseInt(params.get("fps"));
|
||||
if (!isNaN(tmp)) {
|
||||
fps = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
function supportsWebGL() {
|
||||
try {
|
||||
let canvas = document.createElement("canvas");
|
||||
let ctx = canvas.getContext("webgl") || canvas.getContext("experimental-webgl");
|
||||
return ctx != null;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function calculateScale(width, height) {
|
||||
let windowWidth = window.innerWidth;
|
||||
let windowHeight = window.innerHeight;
|
||||
let scaleX = windowWidth / width;
|
||||
let scaleY = windowHeight / height;
|
||||
return { x: scaleX, y: scaleY };
|
||||
};
|
||||
|
||||
if (!supportsWebGL()) {
|
||||
alert('WebGL is unavailable. Fallback image will be used.');
|
||||
var e = document.getElementById("spine-widget");
|
||||
e.classList.add("fallback");
|
||||
e.parentElement.classList.add("widget-wrapper");
|
||||
document.getElementById("loader").style.display = "none";
|
||||
|
||||
function fallback() {
|
||||
const scale = calculateScale(window.operator.fallbackImage.width, window.operator.fallbackImage.height);
|
||||
if (scale.x > scale.y) {
|
||||
e.style.width = window.operator.fallbackImage.width * scale.y;
|
||||
e.style.height = window.operator.fallbackImage.height * scale.y;
|
||||
} else if (scale.x < scale.y) {
|
||||
e.style.width = window.operator.fallbackImage.width * scale.x;
|
||||
e.style.height = window.operator.fallbackImage.height * scale.x;
|
||||
}
|
||||
document.getElementById("logo").width = window.innerWidth / 2 * RATIO
|
||||
}
|
||||
window.addEventListener('resize', fallback, true);
|
||||
fallback();
|
||||
} else {
|
||||
var resetTime = window.performance.now();
|
||||
var e = document.getElementById("spine-widget");
|
||||
var spineWidget;
|
||||
window.isLoaded = false;
|
||||
|
||||
function render() {
|
||||
let isPlayingInteract = false;
|
||||
|
||||
var scale = calculateScale(window.operator.spineSize.width, window.operator.spineSize.height);
|
||||
|
||||
spineWidget = new spine.SpineWidget(e, {
|
||||
atlasContent: atob(window.operator.atlasContent),
|
||||
jsonContent: window.operator.jsonContent,
|
||||
atlasPages: window.operator.atlasPages,
|
||||
atlasPagesContent: window.operator.atlasPagesContent,
|
||||
animation: "Idle",
|
||||
backgroundColor: "#00000000",
|
||||
loop: true,
|
||||
skin: "default",
|
||||
fps: fps,
|
||||
scale: Math.min(scale.x, scale.y),
|
||||
x: window.innerWidth / 2,
|
||||
y: calculateCenterY(scale.x, scale.y),
|
||||
fitToCanvas: false,
|
||||
premultipliedAlpha: true,
|
||||
success: function (widget) {
|
||||
widget.state.addListener({
|
||||
end: (e) => {
|
||||
if (e.animation.name == "Interact") {
|
||||
isPlayingInteract = false;
|
||||
}
|
||||
},
|
||||
complete: (e) => {
|
||||
if (window.performance.now() - resetTime >= 8 * 1000 && Math.random() < 0.3) {
|
||||
resetTime = window.performance.now();
|
||||
let entry = widget.state.setAnimation(0, "Special", true, 0);
|
||||
entry.mixDuration = 0.2;
|
||||
widget.state.addAnimation(0, "Idle", true, 0);
|
||||
}
|
||||
},
|
||||
});
|
||||
widget.canvas.onclick = function () {
|
||||
if (isPlayingInteract) {
|
||||
return;
|
||||
}
|
||||
isPlayingInteract = true;
|
||||
let entry = widget.state.setAnimation(0, "Interact", true, 0);
|
||||
entry.mixDuration = 0.2;
|
||||
widget.state.addAnimation(0, "Idle", true, 0);
|
||||
}
|
||||
document.getElementById("loader").style.display = "none";
|
||||
window.isLoaded = true;
|
||||
}
|
||||
});
|
||||
|
||||
document.getElementById("logo").width = window.innerWidth / 2 * RATIO
|
||||
}
|
||||
|
||||
function calculateCenterY(scaleX, scaleY) {
|
||||
var height = window.innerHeight;
|
||||
var offset = Math.min(window.operator.spineSize.offset, height / window.operator.spineSize.correctionFactor);
|
||||
if (scaleX < scaleY) {
|
||||
// constrained by width
|
||||
var scaledSpineHeight = window.operator.spineSize.height * scaleX;
|
||||
return (height - scaledSpineHeight) / 2 + offset;
|
||||
}
|
||||
return offset;
|
||||
}
|
||||
render();
|
||||
optimizedResize.add(function() {
|
||||
if (window.isLoaded) {
|
||||
window.isLoaded = false;
|
||||
document.getElementById("logo").width = window.innerWidth / 2 * RATIO
|
||||
document.getElementById("loader").style.display = "inherit";
|
||||
var scale = calculateScale(window.operator.spineSize.width, window.operator.spineSize.height);
|
||||
spineWidget.reRender({
|
||||
x: window.innerWidth / 2,
|
||||
y: calculateCenterY(scale.x, scale.y),
|
||||
scale: Math.min(scale.x, scale.y),
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
window.addEventListener("contextmenu", e => e.preventDefault());
|
||||
|
||||
document.addEventListener("gesturestart", e => e.preventDefault());
|
||||
|
||||
// wallpaper engine
|
||||
window.wallpaperPropertyListener = {
|
||||
applyGeneralProperties: function (properties) {
|
||||
if (properties.fps) {
|
||||
spineWidget.setFps(properties.fps);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
console.log("All resources are extracted from Arknights.")
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,117 +0,0 @@
|
||||
html {
|
||||
-ms-user-select: none;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
user-select: none;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
background-size: cover;
|
||||
background-image: url("../assets/operator_bg.png");
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.logo {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 0;
|
||||
filter: invert(1);
|
||||
opacity: 0.3;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
.widget-wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.fallback {
|
||||
margin: auto;
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
background-size: cover;
|
||||
background-image: url("../assets/fallback.png");
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.loader,
|
||||
.loader:before,
|
||||
.loader:after {
|
||||
background: #546e7a;
|
||||
-webkit-animation: load1 1s infinite ease-in-out;
|
||||
animation: load1 1s infinite ease-in-out;
|
||||
width: 1em;
|
||||
height: 4em;
|
||||
}
|
||||
|
||||
.loader {
|
||||
color: #546e7a;
|
||||
text-indent: -9999em;
|
||||
margin: 88px auto;
|
||||
position: relative;
|
||||
font-size: 11px;
|
||||
-webkit-transform: translateZ(0);
|
||||
-ms-transform: translateZ(0);
|
||||
transform: translateZ(0);
|
||||
-webkit-animation-delay: -0.16s;
|
||||
animation-delay: -0.16s;
|
||||
position: fixed;
|
||||
right: 88px;
|
||||
top: 0;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
.loader:before,
|
||||
.loader:after {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
content: '';
|
||||
}
|
||||
|
||||
.loader:before {
|
||||
left: -1.5em;
|
||||
-webkit-animation-delay: -0.32s;
|
||||
animation-delay: -0.32s;
|
||||
}
|
||||
|
||||
.loader:after {
|
||||
left: 1.5em;
|
||||
}
|
||||
|
||||
@-webkit-keyframes load1 {
|
||||
0%,
|
||||
80%,
|
||||
100% {
|
||||
box-shadow: 0 0;
|
||||
height: 4em;
|
||||
}
|
||||
|
||||
40% {
|
||||
box-shadow: 0 -2em;
|
||||
height: 5em;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes load1 {
|
||||
0%,
|
||||
80%,
|
||||
100% {
|
||||
box-shadow: 0 0;
|
||||
height: 4em;
|
||||
}
|
||||
|
||||
40% {
|
||||
box-shadow: 0 -2em;
|
||||
height: 5em;
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
<html lang="en">
|
||||
<!doctype html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
@@ -6,19 +7,21 @@
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black">
|
||||
<meta name="renderer" content="webkit">
|
||||
<title>Skadi the Corrupting Heart</title>
|
||||
<link rel="stylesheet" type="text/css" href="./build/style.css?v1.6.1">
|
||||
<link rel="stylesheet" href="./assets/style.css">
|
||||
<link rel="stylesheet" href="./assets/spine-player.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="dev" style="position: fixed;left: 0;left: 0;background-color: white;"></div>
|
||||
<div class="loader" id="loader">Loading...</div>
|
||||
<image src="./assets/operator_logo.png" class="logo" id="logo" alt="operator logo" />
|
||||
<div id="widget-wrapper">
|
||||
<div id="spine-widget"></div>
|
||||
<image src="./operator/operator_logo.png" class="logo" id="logo" alt="operator logo" />
|
||||
<div id="widget-wrapper" style="width: 100%; height: 100%;">
|
||||
<div id="container" style="width: 100%; height: 100%;"></div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
<script type="text/javascript" src="./build/operator.js?v1.6.1"></script>
|
||||
<script type="text/javascript" src="./build/spine-widget.js?v1.6.1"></script>
|
||||
<script type="text/javascript" src="./build/runner.js?v1.6.1"></script>
|
||||
<script src="./assets/spine-player.js"></script>
|
||||
<script type="text/javascript" src="./operator/operator.js"></script>
|
||||
<script type="text/javascript" src="./operator/settings.js"></script>
|
||||
<script type="text/javascript" src="./assets/runner.js"></script>
|
||||
|
||||
</html>
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Binary file not shown.
|
Before Width: | Height: | Size: 1.9 MiB |
Binary file not shown.
Binary file not shown.
|
Before Width: | Height: | Size: 1.4 MiB |
Binary file not shown.
|
Before Width: | Height: | Size: 3.0 MiB |
Binary file not shown.
|
Before Width: | Height: | Size: 33 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 31 KiB |
File diff suppressed because one or more lines are too long
@@ -1,148 +0,0 @@
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
const RATIO = 0.618;
|
||||
var fps = 60;
|
||||
|
||||
if (params.has("fps")) {
|
||||
var tmp = parseInt(params.get("fps"));
|
||||
if (!isNaN(tmp)) {
|
||||
fps = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
function supportsWebGL() {
|
||||
try {
|
||||
let canvas = document.createElement("canvas");
|
||||
let ctx = canvas.getContext("webgl") || canvas.getContext("experimental-webgl");
|
||||
return ctx != null;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function calculateScale(width, height) {
|
||||
let windowWidth = window.innerWidth;
|
||||
let windowHeight = window.innerHeight;
|
||||
let scaleX = windowWidth / width;
|
||||
let scaleY = windowHeight / height;
|
||||
return { x: scaleX, y: scaleY };
|
||||
};
|
||||
|
||||
if (!supportsWebGL()) {
|
||||
alert('WebGL is unavailable. Fallback image will be used.');
|
||||
var e = document.getElementById("spine-widget");
|
||||
e.classList.add("fallback");
|
||||
e.parentElement.classList.add("widget-wrapper");
|
||||
document.getElementById("loader").style.display = "none";
|
||||
|
||||
function fallback() {
|
||||
const scale = calculateScale(window.operator.fallbackImage.width, window.operator.fallbackImage.height);
|
||||
if (scale.x > scale.y) {
|
||||
e.style.width = window.operator.fallbackImage.width * scale.y;
|
||||
e.style.height = window.operator.fallbackImage.height * scale.y;
|
||||
} else if (scale.x < scale.y) {
|
||||
e.style.width = window.operator.fallbackImage.width * scale.x;
|
||||
e.style.height = window.operator.fallbackImage.height * scale.x;
|
||||
}
|
||||
document.getElementById("logo").width = window.innerWidth / 2 * RATIO
|
||||
}
|
||||
window.addEventListener('resize', fallback, true);
|
||||
fallback();
|
||||
} else {
|
||||
var resetTime = window.performance.now();
|
||||
var e = document.getElementById("spine-widget");
|
||||
var spineWidget;
|
||||
window.isLoaded = false;
|
||||
|
||||
function render() {
|
||||
let isPlayingInteract = false;
|
||||
|
||||
var scale = calculateScale(window.operator.spineSize.width, window.operator.spineSize.height);
|
||||
|
||||
spineWidget = new spine.SpineWidget(e, {
|
||||
atlasContent: atob(window.operator.atlasContent),
|
||||
json: "./assets/dyn_illust_char_1012_skadi2.json",
|
||||
atlasPages: window.operator.atlasPages,
|
||||
atlasPagesContent: window.operator.atlasPagesContent,
|
||||
animation: "Idle",
|
||||
backgroundColor: "#00000000",
|
||||
loop: true,
|
||||
skin: "default",
|
||||
fps: fps,
|
||||
scale: Math.min(scale.x, scale.y),
|
||||
x: window.innerWidth / 2,
|
||||
y: calculateCenterY(scale.x, scale.y),
|
||||
fitToCanvas: false,
|
||||
premultipliedAlpha: true,
|
||||
success: function (widget) {
|
||||
widget.state.addListener({
|
||||
end: (e) => {
|
||||
if (e.animation.name == "Interact") {
|
||||
isPlayingInteract = false;
|
||||
}
|
||||
},
|
||||
complete: (e) => {
|
||||
if (window.performance.now() - resetTime >= 8 * 1000 && Math.random() < 0.3) {
|
||||
resetTime = window.performance.now();
|
||||
let entry = widget.state.setAnimation(0, "Special", true, 0);
|
||||
entry.mixDuration = 0.2;
|
||||
widget.state.addAnimation(0, "Idle", true, 0);
|
||||
}
|
||||
},
|
||||
});
|
||||
widget.canvas.onclick = function () {
|
||||
if (isPlayingInteract) {
|
||||
return;
|
||||
}
|
||||
isPlayingInteract = true;
|
||||
let entry = widget.state.setAnimation(0, "Interact", true, 0);
|
||||
entry.mixDuration = 0.2;
|
||||
widget.state.addAnimation(0, "Idle", true, 0);
|
||||
}
|
||||
document.getElementById("loader").style.display = "none";
|
||||
window.isLoaded = true;
|
||||
}
|
||||
});
|
||||
|
||||
document.getElementById("logo").width = window.innerWidth / 2 * RATIO
|
||||
}
|
||||
|
||||
function calculateCenterY(scaleX, scaleY) {
|
||||
var height = window.innerHeight;
|
||||
var offset = Math.min(window.operator.spineSize.offset, height / window.operator.spineSize.correctionFactor);
|
||||
if (scaleX < scaleY) {
|
||||
// constrained by width
|
||||
var scaledSpineHeight = window.operator.spineSize.height * scaleX;
|
||||
return (height - scaledSpineHeight) / 2 + offset;
|
||||
}
|
||||
return offset;
|
||||
}
|
||||
render();
|
||||
optimizedResize.add(function() {
|
||||
if (window.isLoaded) {
|
||||
window.isLoaded = false;
|
||||
document.getElementById("logo").width = window.innerWidth / 2 * RATIO
|
||||
document.getElementById("loader").style.display = "inherit";
|
||||
var scale = calculateScale(window.operator.spineSize.width, window.operator.spineSize.height);
|
||||
spineWidget.reRender({
|
||||
x: window.innerWidth / 2,
|
||||
y: calculateCenterY(scale.x, scale.y),
|
||||
scale: Math.min(scale.x, scale.y),
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
window.addEventListener("contextmenu", e => e.preventDefault());
|
||||
|
||||
document.addEventListener("gesturestart", e => e.preventDefault());
|
||||
|
||||
// wallpaper engine
|
||||
window.wallpaperPropertyListener = {
|
||||
applyGeneralProperties: function (properties) {
|
||||
if (properties.fps) {
|
||||
spineWidget.setFps(properties.fps);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
console.log("All resources are extracted from Arknights.")
|
||||
@@ -1,117 +0,0 @@
|
||||
html {
|
||||
-ms-user-select: none;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
user-select: none;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
background-size: cover;
|
||||
background-image: url("../assets/operator_bg.png");
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.logo {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 0;
|
||||
filter: invert(1);
|
||||
opacity: 0.3;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
.widget-wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.fallback {
|
||||
margin: auto;
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
background-size: cover;
|
||||
background-image: url("../assets/fallback.png");
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.loader,
|
||||
.loader:before,
|
||||
.loader:after {
|
||||
background: #546e7a;
|
||||
-webkit-animation: load1 1s infinite ease-in-out;
|
||||
animation: load1 1s infinite ease-in-out;
|
||||
width: 1em;
|
||||
height: 4em;
|
||||
}
|
||||
|
||||
.loader {
|
||||
color: #546e7a;
|
||||
text-indent: -9999em;
|
||||
margin: 88px auto;
|
||||
position: relative;
|
||||
font-size: 11px;
|
||||
-webkit-transform: translateZ(0);
|
||||
-ms-transform: translateZ(0);
|
||||
transform: translateZ(0);
|
||||
-webkit-animation-delay: -0.16s;
|
||||
animation-delay: -0.16s;
|
||||
position: fixed;
|
||||
right: 88px;
|
||||
top: 0;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
.loader:before,
|
||||
.loader:after {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
content: '';
|
||||
}
|
||||
|
||||
.loader:before {
|
||||
left: -1.5em;
|
||||
-webkit-animation-delay: -0.32s;
|
||||
animation-delay: -0.32s;
|
||||
}
|
||||
|
||||
.loader:after {
|
||||
left: 1.5em;
|
||||
}
|
||||
|
||||
@-webkit-keyframes load1 {
|
||||
0%,
|
||||
80%,
|
||||
100% {
|
||||
box-shadow: 0 0;
|
||||
height: 4em;
|
||||
}
|
||||
|
||||
40% {
|
||||
box-shadow: 0 -2em;
|
||||
height: 5em;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes load1 {
|
||||
0%,
|
||||
80%,
|
||||
100% {
|
||||
box-shadow: 0 0;
|
||||
height: 4em;
|
||||
}
|
||||
|
||||
40% {
|
||||
box-shadow: 0 -2em;
|
||||
height: 5em;
|
||||
}
|
||||
}
|
||||
@@ -1,77 +0,0 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<script src="./build/spine-player.js"></script>
|
||||
<link rel="stylesheet" href="./build/style.css">
|
||||
<link rel="stylesheet" href="./build/spine-player.css">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="dev" style="position: fixed;left: 0;left: 0;background-color: white;"></div>
|
||||
<div id="container" style="width: 100%; height: 100%;"></div>
|
||||
</body>
|
||||
<script>
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
var fps = 60;
|
||||
|
||||
if (params.has("fps")) {
|
||||
var tmp = parseInt(params.get("fps"));
|
||||
if (!isNaN(tmp)) {
|
||||
fps = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
var resetTime = window.performance.now();
|
||||
let isPlayingInteract = false;
|
||||
new spine.SpinePlayer("container", {
|
||||
jsonUrl: "assets/dyn_illust_char_1012_skadi2.json",
|
||||
atlasUrl: "assets/dyn_illust_char_1012_skadi2.atlas",
|
||||
animation: "Idle",
|
||||
premultipliedAlpha: true,
|
||||
alpha: true,
|
||||
backgroundColor: "#00000000",
|
||||
fps: fps,
|
||||
viewport: {
|
||||
debugRender: false,
|
||||
padLeft: "-5%",
|
||||
padRight: "-10%",
|
||||
padTop: "0%",
|
||||
padBottom: "-12%",
|
||||
x: 0,
|
||||
y: 0,
|
||||
},
|
||||
showControls: false,
|
||||
success: function (widget) {
|
||||
widget.animationState.addListener({
|
||||
end: (e) => {
|
||||
if (e.animation.name == "Interact") {
|
||||
isPlayingInteract = false;
|
||||
}
|
||||
},
|
||||
complete: (e) => {
|
||||
if (window.performance.now() - resetTime >= 8 * 1000 && Math.random() < 0.3) {
|
||||
resetTime = window.performance.now();
|
||||
let entry = widget.animationState.setAnimation(0, "Special", true, 0);
|
||||
entry.mixDuration = 0.2;
|
||||
widget.animationState.addAnimation(0, "Idle", true, 0);
|
||||
}
|
||||
},
|
||||
});
|
||||
widget.canvas.onclick = function () {
|
||||
if (isPlayingInteract) {
|
||||
return;
|
||||
}
|
||||
isPlayingInteract = true;
|
||||
let entry = widget.animationState.setAnimation(0, "Interact", true, 0);
|
||||
entry.mixDuration = 0.2;
|
||||
widget.animationState.addAnimation(0, "Idle", true, 0);
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
Reference in New Issue
Block a user