Skip to content

Commit

Permalink
Feature/add continuous shooting mode (#10)
Browse files Browse the repository at this point in the history
* change to save the image after drawing the image

* change: animation does not start even after changing the initial display and idol, dress, type.

* add: ui for continuous shooting mode

* feat: Implemented continuous shooting mode
  • Loading branch information
darkeroticism committed Apr 1, 2024
1 parent 05bd5f8 commit 79c6bac
Show file tree
Hide file tree
Showing 2 changed files with 66 additions and 30 deletions.
14 changes: 14 additions & 0 deletions main.html
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,20 @@ <h5 class="modal-title" id="divMobileLabel">Mobile Device Detected</h5>
data-name="Right" fill="none" id="Right-2" points="7.9 12.3 12 16.3 16.1 12.3" stroke="#000000" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"></polyline> <line fill="none" stroke="#000000" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" x1="12" x2="12" y1="2.7" y2="14.2"></line> </g> </g> </g> </g> </g></svg>
save
</button>
<div class="form-check form-switch">
<input id="continuousShootingModeSwitch" class="form-check-input" type="checkbox" role="switch" id="flexSwitchCheckDefault">
<label class="form-check-label" for="flexSwitchCheckDefault"><span>Enable continuous shooting</span>
<span type="button" class="badge rounded-pill bg-info" data-bs-toggle="tooltip" data-bs-placement="right"
data-bs-title="Animation does not start when changing idol, costume, or type. The image will be downloaded automatically. Check the animation list’s checkboxes to animate.">
<!-- "Info lg" icon from Bootstrap Icons -->
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-info-lg"
viewBox="0 0 16 16">
<path
d="m9.708 6.075-3.024.379-.108.502.595.108c.387.093.464.232.38.619l-.975 4.577c-.255 1.183.14 1.74 1.067 1.74.72 0 1.554-.332 1.933-.789l.116-.549c-.263.232-.65.325-.905.325-.363 0-.494-.255-.402-.704l1.323-6.208Zm.091-2.755a1.32 1.32 0 1 1-2.64 0 1.32 1.32 0 0 1 2.64 0Z" />
</svg>
</span>
</label>
</div>
</div>
<div class="p-0 col-lg-9 d-flex">
<canvas id="canvas" width="1136" height="640" class="img-fluid"></canvas>
Expand Down
82 changes: 52 additions & 30 deletions main.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ const dropLoader = PIXI.Assets, cont = new PIXI.Container();
const SML0 = "sml_cloth0", SML1 = "sml_cloth1", BIG0 = "big_cloth0", BIG1 = "big_cloth1";
const urlParams = new URLSearchParams(window.location.search);

// State
let isContinuousShootingEnabled = false

const idolMap = new Map();
const spineMap = new Map();

Expand Down Expand Up @@ -83,7 +86,7 @@ async function renderByDrop(dataTexture) {
const spineAtlasLoader = new PIXI.spine.core.AtlasAttachmentLoader(spineAtlas);
const spineJsonParser = new PIXI.spine.core.SkeletonJson(spineAtlasLoader);
const spineData = spineJsonParser.readSkeletonData(rawJson);
setupAnimationList(spineData);
await setupAnimationList(spineData);
}

function toastInit() {
Expand All @@ -98,6 +101,11 @@ function toastInit() {
}
}

function tooltipInit() {
const tooltipTriggerList = document.querySelectorAll('[data-bs-toggle="tooltip"]')
const tooltipList = [...tooltipTriggerList].map(tooltipTriggerEl => new bootstrap.Tooltip(tooltipTriggerEl))
}

function toMobileUI() {
window.location.href = "https://mspine.shinycolors.moe";
}
Expand All @@ -115,6 +123,7 @@ async function init() {
}

toastInit();
tooltipInit();
const canvas = document.getElementById("canvas"), resetBtn = document.getElementById("resetAnimation");

app = new PIXI.Application({
Expand All @@ -134,13 +143,19 @@ async function init() {
resetAllAnimation();
}

const continuousShootingModeSwitch = document.getElementById("continuousShootingModeSwitch")
continuousShootingModeSwitch.addEventListener("change", (event) => {
isContinuousShootingEnabled = event.target.checked
// console.info(`enableContinuousShooting:${isContinuousShootingEnabled}`)
})

fetch("https://api.shinycolors.moe/spine/idollist").then(async (response) => {
const idolInfo = await response.json();
const idolInfoMap = new Map();
idolInfo.forEach((element) => {
idolInfoMap.set(element.idolId, element);
});
setupIdolList(idolInfoMap);
await setupIdolList(idolInfoMap);
});

_hello();
Expand All @@ -158,7 +173,7 @@ function _hello() {
console.log(...log);
}

function setupIdolList(idolInfo) {
async function setupIdolList(idolInfo) {
const idolList = document.getElementById("idolList");
let idolId = urlParams.has("idolId") ? Number(urlParams.get("idolId")) : 1,
idolName = idolInfo.get(idolId).idolName;
Expand All @@ -174,13 +189,13 @@ function setupIdolList(idolInfo) {
idolList.appendChild(option);
});

idolList.onchange = () => {
idolList.onchange = async () => {
idolId = idolList.value;
idolName = idolInfo.get(Number(idolId)).idolName;
testAndLoadDress(idolId, idolName);
await testAndLoadDress(idolId, idolName);
};

testAndLoadDress(idolId, idolName);
await testAndLoadDress(idolId, idolName);
}
/*
function testAndLoadPreset(idolId) {
Expand All @@ -198,27 +213,27 @@ function setupPreset(presetList) {

}

function testAndLoadDress(idolId, idolName) {
async function testAndLoadDress(idolId, idolName) {
if (!idolMap.has(idolName)) {
if (idolId == 0) {
fetch(`https://cf-static.shinycolors.moe/others/hazuki.json`).then(async (response) => {
idolMap.set(idolName, await response.json());
setupDressList(idolMap.get(idolName));
await setupDressList(idolMap.get(idolName));
});
}
else {
fetch(`https://api.shinycolors.moe/spine/dressList?idolId=${idolId}`).then(async (response) => {
idolMap.set(idolName, await response.json());
setupDressList(idolMap.get(idolName));
await setupDressList(idolMap.get(idolName));
});
}
}
else {
setupDressList(idolMap.get(idolName));
await setupDressList(idolMap.get(idolName));
}
}

function setupDressList(idolDressList) {
async function setupDressList(idolDressList) {
const dressList = document.getElementById("dressList");
dressList.innerHTML = "";

Expand Down Expand Up @@ -258,15 +273,15 @@ function setupDressList(idolDressList) {
});
dressList.appendChild(optGroup);

dressList.onchange = () => {
dressList.onchange = async () => {
arrayOrder = dressList.value;
setupTypeList(idolDressList[arrayOrder]);
await setupTypeList(idolDressList[arrayOrder]);
};

setupTypeList(idolDressList[arrayOrder]);
await setupTypeList(idolDressList[arrayOrder]);
}

function setupTypeList(dressObj) {
async function setupTypeList(dressObj) {
const typeList = document.getElementById("typeList");
let dressType;
typeList.innerHTML = "";
Expand Down Expand Up @@ -351,50 +366,50 @@ function setupTypeList(dressObj) {
}
}

typeList.onchange = () => {
typeList.onchange = async () => {
const dressList = document.getElementById("dressList");
dressType = typeList.value;

if (dressObj.idolId == 0) {
testAndLoadAnimation(dressList.options[dressList.selectedIndex].getAttribute("path"), dressType, true);
await testAndLoadAnimation(dressList.options[dressList.selectedIndex].getAttribute("path"), dressType, true);
}
else {
testAndLoadAnimation(dressList.options[dressList.selectedIndex].getAttribute("enzaId"), dressType);
await testAndLoadAnimation(dressList.options[dressList.selectedIndex].getAttribute("enzaId"), dressType);
}
};

if (dressObj.idolId == 0) {
testAndLoadAnimation(dressObj.path, dressType, true);
await testAndLoadAnimation(dressObj.path, dressType, true);
}
else {
testAndLoadAnimation(dressObj.enzaId, dressType);
await testAndLoadAnimation(dressObj.enzaId, dressType);
}

}

function testAndLoadAnimation(enzaId, type, flag = false) {
async function testAndLoadAnimation(enzaId, type, flag = false) {
if (!spineMap.has(`${enzaId}/${type}`)) {
if (flag) {
PIXI.Assets.load(`https://cf-static.shinycolors.moe/spine/sub_characters/${migrateMap[type]}/${enzaId}`).then((resource) => {
PIXI.Assets.load(`https://cf-static.shinycolors.moe/spine/sub_characters/${migrateMap[type]}/${enzaId}`).then(async (resource) => {
const waifu = resource.spineData;
spineMap.set(`${enzaId}/${type}`, waifu);
setupAnimationList(waifu);
await setupAnimationList(waifu);
});
}
else {
PIXI.Assets.load(`https://cf-static.shinycolors.moe/spine/idols/${migrateMap[type]}/${enzaId}/data.json`).then((resource) => {
PIXI.Assets.load(`https://cf-static.shinycolors.moe/spine/idols/${migrateMap[type]}/${enzaId}/data.json`).then(async (resource) => {
const waifu = resource.spineData;
spineMap.set(`${enzaId}/${type}`, waifu);
setupAnimationList(waifu);
await setupAnimationList(waifu);
});
}
}
else {
setupAnimationList(spineMap.get(`${enzaId}/${type}`));
await setupAnimationList(spineMap.get(`${enzaId}/${type}`));
}
}

function setupAnimationList(spineData) {
async function setupAnimationList(spineData) {
const animationList = document.getElementById("divAnimationBody");
animationList.innerHTML = "";

Expand Down Expand Up @@ -447,7 +462,7 @@ function setupAnimationList(spineData) {
currentSpine.state.setAnimation(0, currentSpine.spineData.animations[0].name, true);
}

renderToStage(currentSpine);
await renderToStage(currentSpine);
}

function animationOnChange(theInput, trackNo, currentSpine) {
Expand All @@ -470,8 +485,13 @@ function blobToBase64(blob) {
reader.readAsDataURL(blob);
});
}

function renderToStage(currentSpine) {
const clearState = (spine) => {
spine.state.clearTracks();
spine.skeleton.setToSetupPose();
spine.lastTime = null;
};
async function renderToStage(currentSpine) {
if (isContinuousShootingEnabled) { clearState(currentSpine) }
cont.removeChild(cont.children[0]);
cont.addChild(currentSpine);

Expand All @@ -497,6 +517,8 @@ function renderToStage(currentSpine) {
cont.scale.set(scale);
cont.pivot.set(contLocalBound.width / 2, contLocalBound.height / 2);
cont.position.set(app.view.width / 2, app.view.height / 2);

if (isContinuousShootingEnabled) { await saveImage(); }
}

function resetAllAnimation() {
Expand Down

0 comments on commit 79c6bac

Please sign in to comment.