Widget:PtNLive2DJolyne: Difference between revisions

From JoJo's Bizarre Encyclopedia - JoJo Wiki
Jump to navigation Jump to search
No edit summary
No edit summary
 
(75 intermediate revisions by 2 users not shown)
Line 1: Line 1:
<noinclude>Live2D Embed for ''[[Path to Nowhere/Jolyne Cujoh]]''.
<noinclude>Live2D Embed for ''[[Path to Nowhere/Jolyne Cujoh]]''. Used by {{Tl|PtNLive2DJolyne}}.
</noinclude><includeonly>
[[Category:Path to Nowhere Templates]]</noinclude><includeonly>
<div class="modal-template"><span class="mcbutton mw-ui-button modal-button white" style="width:auto; height:auto; line-height: 1; margin-left: auto; margin-right: auto;">Live2D</span><div class="modal" style="display:none"><div class="modal-content" style="width:max-content"><div class="modal-header"><span class="close">&times;</span></div><div class="modal-body volume"><div id="ptn-canvas-container" style="margin: 0 auto;"><canvas id="ptn-canvas"></canvas></div></div></div></div></div>
<div class="modal-template"><span class="mcbutton mw-ui-button modal-button white" style="width:auto; height:auto; line-height: 1; margin-left: auto; margin-right: auto;">Live2D Animation</span><div class="modal" style="display:none"><div class="modal-content" style="width:max-content; background: none; background-image: none; padding: none; border: none; margin-top: 0;"><div class="modal-header"><span class="close">&times;</span></div><div class="modal-body" style="background-color:transparent; border: none; padding: 0; margin: 0;"><div id="ptn-canvas-container" style="margin: 0 auto;"><canvas id="ptn-canvas"></canvas></div><div id="ptn-canvas-caption" style="background:rgba(0, 0, 0, 0.8); display: none; z-index:1000; color: white; width: 50%; position: absolute; bottom: 20px; left: 0; right: 0; margin: 0 auto; text-align: center;"></div></div></div></div></div>
<script type="text/javascript">
<script src="https://cubism.live2d.com/sdk-web/cubismcore/live2dcubismcore.min.js" crossorigin="anonymous"></script>
let resizeCanvasToModel;
<script src="https://cdn.jsdelivr.net/gh/dylanNew/live2d/webgl/Live2D/lib/live2d.min.js" crossorigin="anonymous"></script>
let captionDiv;
<script src="https://cdn.jsdelivr.net/npm/pixi.js@7.x/dist/pixi.min.js" crossorigin="anonymous"></script>
<script src="https://jojowiki.com/customizations/Live2D/assets/cubism4.min.js"></script>
<script>
const live2d = PIXI.live2d;


document.querySelector(".modal-button").addEventListener("click", () => {
(async function main() {
const app = new PIXI.Application({
// Check if the canvas exists; recreate it if necessary
view: document.getElementById("ptn-canvas"),
const container = document.getElementById("ptn-canvas-container");
let canvas = document.getElementById("ptn-canvas");
autoStart: true,
backgroundAlpha: 0,
});
app.ticker.maxFPS = 30;
app.renderer.resolution = Math.min(window.devicePixelRatio, 1);


if (!canvas) {
const jolyne = await live2d.Live2DModel.from('https://jojowiki.com/customizations/Live2D/resources/model/char2d_jolyne_2/char2d_jolyne_2.model3.json', { idleMotionGroup: 'clip_jolyne_background' });
app.stage.addChild(jolyne);
canvas = document.createElement("canvas");
canvas.id = "ptn-canvas";
container.appendChild(canvas);
}
// Check if Live2D scripts are already loaded
if (!window.PIXI || !window.live2d) {
const scripts = [
"https://cubism.live2d.com/sdk-web/cubismcore/live2dcubismcore.min.js",
"https://cdn.jsdelivr.net/gh/dylanNew/live2d/webgl/Live2D/lib/live2d.min.js",
"https://cdn.jsdelivr.net/npm/pixi.js@7.x/dist/pixi.min.js",
"https://jojowiki.com/customizations/Live2D/assets/cubism4.min.js",
];


function loadScriptsSequentially(scripts, callback) {
// Store the original model dimensions
jolyne.originalWidth = jolyne.width;
if (scripts.length === 0) {
jolyne.originalHeight = jolyne.height;
callback();
return;
}
const script = document.createElement("script");
script.src = scripts.shift();
script.crossOrigin = "anonymous";
script.onload = () => {
console.log(`${script.src} loaded.`);
loadScriptsSequentially(scripts, callback);
};
script.onerror = () => {
console.error(`Failed to load ${script.src}`);
};
document.body.appendChild(script);
}


loadScriptsSequentially(scripts, initializeLive2D);
const container = document.getElementById('ptn-canvas-container');
} else {
const canvas = document.getElementById('ptn-canvas');
initializeLive2D();
canvas.style.touchAction = "auto";
}


function initializeLive2D() {
// Function to resize the canvas and center the container
function resizeCanvasToModel() {
const live2d = PIXI.live2d;
const isMobile = window.innerWidth < 1280; // Define a mobile breakpoint
const scaleFactor = isMobile ? 2 : 1.0; // Scale larger on mobile


(async function () {
// Calculate the scale to fit the model within the window dimensions
const scaleX = (window.innerWidth * scaleFactor) / jolyne.originalWidth;
const canvas = document.getElementById("ptn-canvas");
captionDiv = document.getElementById("ptn-canvas-caption");
const scaleY = (window.innerHeight * scaleFactor) / jolyne.originalHeight;
const scale = Math.min(scaleX, scaleY);
if (!canvas) {
console.error("Canvas element is missing. Cannot initialize Live2D.");
return;
}


// Apply the scale to the model
const app = new PIXI.Application({
jolyne.scale.set(scale);
view: canvas,
autoStart: true,
backgroundAlpha: 0,
});


// Resize the canvas to match the scaled model dimensions
window.live2DApp = app;
canvas.width = jolyne.width;
canvas.height = jolyne.height;


// Set container size
app.ticker.maxFPS = 30;
container.style.width = `${canvas.width}px`;
app.renderer.resolution = Math.min(window.devicePixelRatio, 1);
container.style.height = `${canvas.height}px`;


// Adjust the renderer to match the new canvas dimensions
const jolyne = await live2d.Live2DModel.from(
'https://jojowiki.com/customizations/Live2D/resources/model/char2d_jolyne_2/char2d_jolyne_2.model3.json',
app.renderer.resize(canvas.width, canvas.height);
{ idleMotionGroup: 'clip_jolyne_background' }
}
);
console.log("Live2D model initialized: Jolyne Cujoh");
app.stage.addChild(jolyne);


// Initial resizing
window.live2DModel = jolyne;
resizeCanvasToModel();
// Store the original model dimensions for resizing logic
jolyne.originalWidth = jolyne.width;
jolyne.originalHeight = jolyne.height;


// Add listeners for resizing
resizeCanvasToModel = function () {
window.addEventListener('resize', resizeCanvasToModel);
const isMobile = window.innerWidth<1280;
const scaleFactor = isMobile ? 1.5 : 1.0;
document.querySelector(".modal").addEventListener("show", () => {
app.renderer.resize(canvas.width, canvas.height);
});


const bodyMotions = ["clip_jolyne_click_body", "clip_jolyne_greet", "clip_jolyne_boring"];
const scaleX = (window.innerWidth * scaleFactor) / jolyne.originalWidth;
const scaleY = (window.innerHeight * scaleFactor) / jolyne.originalHeight;
const headMotions = ["clip_jolyne_click_head", "clip_jolyne_irritated", "clip_jolyne_special"];
const handMotions = ["clip_jolyne_click_hand", "clip_jolyne_level"];
const scale = Math.min(scaleX, scaleY);
const legMotions = ["clip_jolyne_click_leg", "clip_jolyne_fail"];


// Function to play a random motion
jolyne.scale.set(scale);
function playRandomMotion(model, motionGroup) {
const randomIndex = Math.floor(Math.random() * motionGroup.length);
const randomMotion = motionGroup[randomIndex];
const motionData = jolyne.internalModel.settings.motions[randomMotion];
if (!motionData || motionData.length === 0) return;


const motionSound = motionData[0].Sound;
canvas.width = jolyne.width;
canvas.height = jolyne.height;


jolyne.parallelMotion([
const container = document.getElementById("ptn-canvas-container");
container.style.width = `${canvas.width}px`;
{ group: 'clip_jolyne_background', index: 0, priority: 1 },
{ group: randomMotion, index: 0, priority: 2 },
container.style.height = `${canvas.height}px`;
]);


if (motionSound) {
app.renderer.resize(canvas.width, canvas.height);
};
console.log(`Playing motion: ${randomMotion}, Sound: ${motionSound}`);

jolyne.speak(motionSound);
}
resizeCanvasToModel();
window.addEventListener("resize", resizeCanvasToModel);
}

// Define motions
const bodyMotions = ["clip_jolyne_click_body", "clip_jolyne_greet", "clip_jolyne_boring"];
const headMotions = ["clip_jolyne_click_head", "clip_jolyne_irritated", "clip_jolyne_special"];
const handMotions = ["clip_jolyne_click_hand", "clip_jolyne_level"];
const legMotions = ["clip_jolyne_click_leg", "clip_jolyne_fail"];
function playRandomMotion(model, motionGroup) {
const randomIndex = Math.floor(Math.random() * motionGroup.length);
const randomMotion = motionGroup[randomIndex];
const motionData = jolyne.internalModel.settings.motions[randomMotion];
if (!motionData || motionData.length === 0) return;


// Loop idle motion
const motionSound = motionData[0].Sound;
const motionCaption = motionData[0].Caption;
function loopIdleMotion() {
if (!jolyne.internalModel.motionManager.isPlaying) {
jolyne.parallelMotion([
jolyne.parallelMotion([
{ group: 'clip_jolyne_background', index: 0, priority: 1 },
{ group: 'clip_jolyne_background', index: 0, priority: 1 },
{ group: 'clip_jolyne_idle', index: 0, priority: 1 },
{ group: randomMotion, index: 0, priority: 2 },
]);
]);

if (motionSound) {
jolyne.speak(motionSound, {
onFinish: function () {
removeCaptions();
},
});

// Set the caption text and make it visible
if (motionCaption && jolyne.internalModel.parallelMotionManager[1].manager.currentAudio) {
if (jolyne.internalModel.parallelMotionManager[1].manager.currentAudio.src == motionSound) {
captionDiv.innerText = motionCaption;
captionDiv.style.display = "block";
}
}
}
else {
removeCaptions();
}
}
}
setTimeout(loopIdleMotion, 1000);
}


loopIdleMotion();
function loopIdleMotion() {
if (!jolyne.internalModel || !jolyne.internalModel.motionManager) {
console.log("Idle motion stopped: Live2D model not available.");
return;
}


if (!jolyne.internalModel.motionManager.isPlaying) {
// Handle tapping
jolyne.parallelMotion([
jolyne.on("hit", (hitAreas) => {
{ group: 'clip_jolyne_background', index: 0, priority: 1 },
if (hitAreas.includes("Body")) {
playRandomMotion(jolyne, bodyMotions);
{ group: 'clip_jolyne_idle', index: 0, priority: 1 },
]);
}
setTimeout(loopIdleMotion, 1000);
}
}

if (hitAreas.includes("Head")) {
playRandomMotion(jolyne, headMotions);
loopIdleMotion();

jolyne.on("hit", (hitAreas) => {
if (hitAreas.includes("Body")) {
playRandomMotion(jolyne, bodyMotions);
}
if (hitAreas.includes("Head")) {
playRandomMotion(jolyne, headMotions);
}
if (hitAreas.includes("HandRight") || hitAreas.includes("HandLeft")) {
playRandomMotion(jolyne, handMotions);
}
if (hitAreas.includes("LegRight") || hitAreas.includes("LegLeft")) {
playRandomMotion(jolyne, legMotions);
}
});

document.addEventListener("visibilitychange", () => {
app.ticker.stop();
if (!document.hidden) app.ticker.start();
});
})();
}
function removeCaptions() {
if (captionDiv) {
captionDiv.innerText = "";
captionDiv.style.display = "none";
}
}

function destroyLive2D() {
if (window.live2DApp) {
window.live2DApp.ticker.stop();

if (window.live2DModel) {
window.live2DModel.destroy({ children: true, texture: true, baseTexture: true });
window.live2DModel = null;
}
}

if (hitAreas.includes("HandRight") || hitAreas.includes("HandLeft")) {
playRandomMotion(jolyne, handMotions);
window.live2DApp.destroy(true, { children: true, texture: true, baseTexture: true });
window.live2DApp = null;
removeCaptions();

if (resizeCanvasToModel) {
window.removeEventListener("resize", resizeCanvasToModel);
}
}
if (hitAreas.includes("LegRight") || hitAreas.includes("LegLeft")) {
playRandomMotion(jolyne, legMotions);
}
});
document.addEventListener("visibilitychange", () => {
app.ticker.stop(); // Pause rendering when the tab is inactive
if (!document.hidden) app.ticker.start(); // Resume rendering when active
});


console.log("Live2D resources destroyed.");
})();
</script>
}
}

const modal = document.querySelector(".modal");
modal.addEventListener("click", (event) => {
if (event.target === modal) {
destroyLive2D();
modal.style.display = "none";
}
});

document.querySelector(".close").addEventListener("click", () => {
destroyLive2D();
});
});
</script>
</includeonly>
</includeonly>

Latest revision as of 21:05, 10 December 2024

Live2D Embed for Path to Nowhere/Jolyne Cujoh. Used by PtNLive2DJolyne.