Difference between revisions of "MediaWiki:Gadget-artTab.js"
Jump to navigation
Jump to search
Pianoforte (talk | contribs) |
(First try making a progress bar) |
||
Line 26: | Line 26: | ||
var stage = view.data('live2dStage'); | var stage = view.data('live2dStage'); | ||
stage.removeChildren(); | stage.removeChildren(); | ||
− | loadLive2dGirl(tdoll, costume, variant, function(live2dData) { | + | loadLive2dGirl($(artTabDiv), tdoll, costume, variant, function(live2dData) { |
view.closest(".artTab").removeClass("loading"); | view.closest(".artTab").removeClass("loading"); | ||
stage.addChild(live2dData); | stage.addChild(live2dData); | ||
Line 70: | Line 70: | ||
} | } | ||
− | function loadLive2dGirl(tdoll, costume, variant, callback) { | + | function base64ArrayBuffer(arrayBuffer) { |
+ | var base64 = '' | ||
+ | var encodings = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' | ||
+ | |||
+ | var bytes = new Uint8Array(arrayBuffer) | ||
+ | var byteLength = bytes.byteLength | ||
+ | var byteRemainder = byteLength % 3 | ||
+ | var mainLength = byteLength - byteRemainder | ||
+ | |||
+ | var a, b, c, d | ||
+ | var chunk | ||
+ | |||
+ | // Main loop deals with bytes in chunks of 3 | ||
+ | for (var i = 0; i < mainLength; i = i + 3) { | ||
+ | // Combine the three bytes into a single integer | ||
+ | chunk = (bytes[i] << 16) | (bytes[i + 1] << 8) | bytes[i + 2] | ||
+ | |||
+ | // Use bitmasks to extract 6-bit segments from the triplet | ||
+ | a = (chunk & 16515072) >> 18 // 16515072 = (2^6 - 1) << 18 | ||
+ | b = (chunk & 258048) >> 12 // 258048 = (2^6 - 1) << 12 | ||
+ | c = (chunk & 4032) >> 6 // 4032 = (2^6 - 1) << 6 | ||
+ | d = chunk & 63 // 63 = 2^6 - 1 | ||
+ | |||
+ | // Convert the raw binary segments to the appropriate ASCII encoding | ||
+ | base64 += encodings[a] + encodings[b] + encodings[c] + encodings[d] | ||
+ | } | ||
+ | |||
+ | // Deal with the remaining bytes and padding | ||
+ | if (byteRemainder == 1) { | ||
+ | chunk = bytes[mainLength] | ||
+ | |||
+ | a = (chunk & 252) >> 2 // 252 = (2^6 - 1) << 2 | ||
+ | |||
+ | // Set the 4 least significant bits to zero | ||
+ | b = (chunk & 3) << 4 // 3 = 2^2 - 1 | ||
+ | |||
+ | base64 += encodings[a] + encodings[b] + '==' | ||
+ | } else if (byteRemainder == 2) { | ||
+ | chunk = (bytes[mainLength] << 8) | bytes[mainLength + 1] | ||
+ | |||
+ | a = (chunk & 64512) >> 10 // 64512 = (2^6 - 1) << 10 | ||
+ | b = (chunk & 1008) >> 4 // 1008 = (2^6 - 1) << 4 | ||
+ | |||
+ | // Set the 2 least significant bits to zero | ||
+ | c = (chunk & 15) << 2 // 15 = 2^4 - 1 | ||
+ | |||
+ | base64 += encodings[a] + encodings[b] + encodings[c] + '=' | ||
+ | } | ||
+ | |||
+ | return base64 | ||
+ | } | ||
+ | |||
+ | function makeBase64Request(url) { | ||
+ | let xhr = new XMLHttpRequest(); | ||
+ | let prom = new Promise(function (resolve, reject) { | ||
+ | xhr.open('GET', url, true); | ||
+ | xhr.responseType = 'arraybuffer'; | ||
+ | xhr.onload = function () { | ||
+ | if (this.status >= 200 && this.status < 300) { | ||
+ | resolve(base64ArrayBuffer(xhr.response)); | ||
+ | } else { | ||
+ | reject({ | ||
+ | status: this.status, | ||
+ | statusText: xhr.statusText | ||
+ | }); | ||
+ | } | ||
+ | }; | ||
+ | xhr.onerror = function () { | ||
+ | reject({ | ||
+ | status: this.status, | ||
+ | statusText: xhr.statusText | ||
+ | }); | ||
+ | }; | ||
+ | }); | ||
+ | |||
+ | prom.startProcess = function(param) { xhr.send(param); }; | ||
+ | |||
+ | return prom; | ||
+ | } | ||
+ | |||
+ | |||
+ | function hideProgress(container) { | ||
+ | let progress = $(container).find('.live2dloadingprogress').first(); | ||
+ | progress.hide(); | ||
+ | } | ||
+ | |||
+ | function showProgress(container, done, length) { | ||
+ | let progress = $(container).find('.live2dloadingprogress').first(); | ||
+ | |||
+ | if (progress.length == 0) { | ||
+ | let centeringFix = $('<span></span>'); | ||
+ | let live2dprogress = $('<div></div>').addClass("live2dprogress").append(centeringFix); | ||
+ | let live2dprogressbar = $('<div></div>').addClass("live2dprogressbar"); | ||
+ | let live2dprogressbarcontainer = $('<div></div>').addClass("live2dprogressbarcontainer").append(live2dprogressbar); | ||
+ | progress = $('<div></div>').addClass("live2dloadingprogress").append(live2dprogress).append(live2dprogressbarcontainer); | ||
+ | $(container).append(progress); | ||
+ | } | ||
+ | |||
+ | progress.show(); | ||
+ | |||
+ | let progressAmount = parseFloat(done / length * 100).toFixed(2); | ||
+ | progress.find('.live2dprogress span').text(progressAmount); | ||
+ | progress.find('.live2dprogressbar').css('width', progressAmount + "%"); | ||
+ | } | ||
+ | |||
+ | function loadLive2dGirl(container, tdoll, costume, variant, callback) { | ||
var modelId = tdoll; | var modelId = tdoll; | ||
if (costume.length > 0) { | if (costume.length > 0) { | ||
Line 81: | Line 186: | ||
$.ajax({ | $.ajax({ | ||
dataType: "json", | dataType: "json", | ||
+ | mimeType: "text/plain", | ||
url: curl(modelId + "_live2d_model.txt"), | url: curl(modelId + "_live2d_model.txt"), | ||
− | |||
success: function (data) { | success: function (data) { | ||
+ | // Init | ||
+ | var counter = 0; | ||
+ | var maxItems = 0; | ||
+ | var confun = function() { | ||
+ | counter++; | ||
+ | showProgress(container, counter, maxItems) | ||
+ | } | ||
+ | |||
+ | confun | ||
+ | |||
json = data; | json = data; | ||
+ | |||
+ | var loadingJobs = new Array(); | ||
+ | |||
+ | // Correct paths | ||
json.model = curl(modelId + "_live2d_model.moc" + ".skel"); | json.model = curl(modelId + "_live2d_model.moc" + ".skel"); | ||
− | if (json.hasOwnProperty('physics')) { json.physics = curl(modelId + " | + | if (json.hasOwnProperty('physics')) { json.physics = curl(modelId + "_live2d_" + json.physics + ".txt"; } |
− | if (json.hasOwnProperty('pose')) { json.pose = curl(modelId + | + | if (json.hasOwnProperty('pose')) { json.pose = curl(modelId + json.pose + ".txt"; } |
− | for (var idx= | + | for (var idx=json.motions.idle.length-1; idx >= 0; idx--) { |
− | json. | + | let job = makeBase64Request(curl(modelId + json.motions.idle[idx].file + ".txt")); |
+ | job.then(function(promiseData) { | ||
+ | let _datatoPush = "data:text/plain;base64," + promiseData; | ||
+ | json.motions.idle.push({ "file": _datatoPush }); | ||
+ | confun(); | ||
+ | }); | ||
+ | json.motions.idle.splice(idx, 1); | ||
+ | loadingJobs.push(job); | ||
+ | |||
} | } | ||
− | for (var idx= | + | for (var idx=json.motions.tap_figure.length-1; idx >= 0 ; idx--) { |
− | json.motions. | + | // json.motions.tap_figure[idx].file = folder + json.motions.tap_figure[idx].file + ".txt"; |
+ | let job = makeBase64Request(curl(modelId + json.motions.tap_figure[idx].file + ".txt")); | ||
+ | job.then(function(promiseData) { | ||
+ | let _datatoPush = "data:text/plain;base64," + promiseData; | ||
+ | json.motions.tap_figure.push({ "file": _datatoPush }); | ||
+ | confun(); | ||
+ | }); | ||
+ | json.motions.tap_figure.splice(idx, 1); | ||
+ | loadingJobs.push(job); | ||
} | } | ||
− | for (var idx=0; idx < json. | + | for (var idx=0; idx < json.textures.length; idx++) { |
− | json. | + | let job = makeBase64Request(curl(modelId + json.textures[idx])); |
+ | job.then(function(promiseData) { | ||
+ | let _datatoPush = "data:image/png;base64," + promiseData; | ||
+ | json.textures.push(_datatoPush); | ||
+ | confun(); | ||
+ | }); | ||
+ | json.textures.splice(idx, 1); | ||
+ | loadingJobs.push(job); | ||
} | } | ||
− | var | + | // Ajaxing |
+ | maxItems = loadingJobs.length; | ||
+ | for (var idxXhr=0; idxXhr < loadingJobs.length; idxXhr++) { loadingJobs[idxXhr].startProcess(null); } | ||
− | + | // Do it | |
+ | $.when.apply($, loadingJobs) | ||
+ | .done(function(x) { | ||
+ | startLive2dStuff(json); | ||
+ | }) | ||
+ | .fail(function(x) { console.log("Failed ajax", x); }) | ||
+ | .always(function(x) { hideProgress(container); }); | ||
} | } | ||
}); | }); |
Revision as of 23:52, 16 January 2018
function switchVariant(artTabDivElement, variant) {
var artTabDiv = $(artTabDivElement);
artTabDiv.find('.artTabLinks').removeClass('active');
artTabDiv.attr('data-tdoll-variant', variant);
refreshView(artTabDiv);
// Set "active" at the end
artTabDiv.find(".artTabLinks[data-tdoll-variant='" + variant + "']").addClass('active');
}
function refreshView(artTabDiv) {
var tdoll = $(artTabDiv).data('tdollId');
var costume = $(artTabDiv).data('tdollCostume');
var variant = $(artTabDiv).attr('data-tdoll-variant'); // why not data though?
$(artTabDiv).find('.artTabContent .fullart').hide();
var live2dActive = $(artTabDiv).find('.live2dstage').is(':visible');
$(artTabDiv).addClass("loading");
if (live2dActive) {
createLive2dView(artTabDiv, function(view) {
view.show();
var stage = view.data('live2dStage');
stage.removeChildren();
loadLive2dGirl($(artTabDiv), tdoll, costume, variant, function(live2dData) {
view.closest(".artTab").removeClass("loading");
stage.addChild(live2dData);
});
});
} else {
$(artTabDiv).find(".fullart[data-variant='" + variant + "']").show();
$(artTabDiv).removeClass("loading");
}
}
function create_url(base, file) {
return base + window.gfUtils.createWikiPathPart(file) + file;
}
function curl(file) {
return create_url("../images/", file);
}
function createLive2dSprite(model) {
// Live 2D Sprite
var l2d_tdoll_sprite = new PIXI.Live2DSprite(model, {
debugLog: false,
randomMotion: false,
eyeBlink: true
});
l2d_tdoll_sprite.startRandomMotion('idle');
l2d_tdoll_sprite.on('mousemove', function(evt) {
const point = evt.data.global;
l2d_tdoll_sprite.setViewPoint(point.x, point.y);
});
l2d_tdoll_sprite.on('click', function(evt) {
const point = evt.data.global;
if (l2d_tdoll_sprite.hitTest('body', point.x, point.y)) {
l2d_tdoll_sprite.startRandomMotionOnce('tap_figure');
// Info: Maybe add individual hit tests?
} else {
l2d_tdoll_sprite.startRandomMotionOnce('tap_figure');
}
});
return l2d_tdoll_sprite;
}
function base64ArrayBuffer(arrayBuffer) {
var base64 = ''
var encodings = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
var bytes = new Uint8Array(arrayBuffer)
var byteLength = bytes.byteLength
var byteRemainder = byteLength % 3
var mainLength = byteLength - byteRemainder
var a, b, c, d
var chunk
// Main loop deals with bytes in chunks of 3
for (var i = 0; i < mainLength; i = i + 3) {
// Combine the three bytes into a single integer
chunk = (bytes[i] << 16) | (bytes[i + 1] << 8) | bytes[i + 2]
// Use bitmasks to extract 6-bit segments from the triplet
a = (chunk & 16515072) >> 18 // 16515072 = (2^6 - 1) << 18
b = (chunk & 258048) >> 12 // 258048 = (2^6 - 1) << 12
c = (chunk & 4032) >> 6 // 4032 = (2^6 - 1) << 6
d = chunk & 63 // 63 = 2^6 - 1
// Convert the raw binary segments to the appropriate ASCII encoding
base64 += encodings[a] + encodings[b] + encodings[c] + encodings[d]
}
// Deal with the remaining bytes and padding
if (byteRemainder == 1) {
chunk = bytes[mainLength]
a = (chunk & 252) >> 2 // 252 = (2^6 - 1) << 2
// Set the 4 least significant bits to zero
b = (chunk & 3) << 4 // 3 = 2^2 - 1
base64 += encodings[a] + encodings[b] + '=='
} else if (byteRemainder == 2) {
chunk = (bytes[mainLength] << 8) | bytes[mainLength + 1]
a = (chunk & 64512) >> 10 // 64512 = (2^6 - 1) << 10
b = (chunk & 1008) >> 4 // 1008 = (2^6 - 1) << 4
// Set the 2 least significant bits to zero
c = (chunk & 15) << 2 // 15 = 2^4 - 1
base64 += encodings[a] + encodings[b] + encodings[c] + '='
}
return base64
}
function makeBase64Request(url) {
let xhr = new XMLHttpRequest();
let prom = new Promise(function (resolve, reject) {
xhr.open('GET', url, true);
xhr.responseType = 'arraybuffer';
xhr.onload = function () {
if (this.status >= 200 && this.status < 300) {
resolve(base64ArrayBuffer(xhr.response));
} else {
reject({
status: this.status,
statusText: xhr.statusText
});
}
};
xhr.onerror = function () {
reject({
status: this.status,
statusText: xhr.statusText
});
};
});
prom.startProcess = function(param) { xhr.send(param); };
return prom;
}
function hideProgress(container) {
let progress = $(container).find('.live2dloadingprogress').first();
progress.hide();
}
function showProgress(container, done, length) {
let progress = $(container).find('.live2dloadingprogress').first();
if (progress.length == 0) {
let centeringFix = $('<span></span>');
let live2dprogress = $('<div></div>').addClass("live2dprogress").append(centeringFix);
let live2dprogressbar = $('<div></div>').addClass("live2dprogressbar");
let live2dprogressbarcontainer = $('<div></div>').addClass("live2dprogressbarcontainer").append(live2dprogressbar);
progress = $('<div></div>').addClass("live2dloadingprogress").append(live2dprogress).append(live2dprogressbarcontainer);
$(container).append(progress);
}
progress.show();
let progressAmount = parseFloat(done / length * 100).toFixed(2);
progress.find('.live2dprogress span').text(progressAmount);
progress.find('.live2dprogressbar').css('width', progressAmount + "%");
}
function loadLive2dGirl(container, tdoll, costume, variant, callback) {
var modelId = tdoll;
if (costume.length > 0) {
modelId = modelId + "_" + costume;
}
if (variant && variant.length > 0) {
modelId = modelId + "_" + variant;
}
$.ajax({
dataType: "json",
mimeType: "text/plain",
url: curl(modelId + "_live2d_model.txt"),
success: function (data) {
// Init
var counter = 0;
var maxItems = 0;
var confun = function() {
counter++;
showProgress(container, counter, maxItems)
}
confun
json = data;
var loadingJobs = new Array();
// Correct paths
json.model = curl(modelId + "_live2d_model.moc" + ".skel");
if (json.hasOwnProperty('physics')) { json.physics = curl(modelId + "_live2d_" + json.physics + ".txt"; }
if (json.hasOwnProperty('pose')) { json.pose = curl(modelId + json.pose + ".txt"; }
for (var idx=json.motions.idle.length-1; idx >= 0; idx--) {
let job = makeBase64Request(curl(modelId + json.motions.idle[idx].file + ".txt"));
job.then(function(promiseData) {
let _datatoPush = "data:text/plain;base64," + promiseData;
json.motions.idle.push({ "file": _datatoPush });
confun();
});
json.motions.idle.splice(idx, 1);
loadingJobs.push(job);
}
for (var idx=json.motions.tap_figure.length-1; idx >= 0 ; idx--) {
// json.motions.tap_figure[idx].file = folder + json.motions.tap_figure[idx].file + ".txt";
let job = makeBase64Request(curl(modelId + json.motions.tap_figure[idx].file + ".txt"));
job.then(function(promiseData) {
let _datatoPush = "data:text/plain;base64," + promiseData;
json.motions.tap_figure.push({ "file": _datatoPush });
confun();
});
json.motions.tap_figure.splice(idx, 1);
loadingJobs.push(job);
}
for (var idx=0; idx < json.textures.length; idx++) {
let job = makeBase64Request(curl(modelId + json.textures[idx]));
job.then(function(promiseData) {
let _datatoPush = "data:image/png;base64," + promiseData;
json.textures.push(_datatoPush);
confun();
});
json.textures.splice(idx, 1);
loadingJobs.push(job);
}
// Ajaxing
maxItems = loadingJobs.length;
for (var idxXhr=0; idxXhr < loadingJobs.length; idxXhr++) { loadingJobs[idxXhr].startProcess(null); }
// Do it
$.when.apply($, loadingJobs)
.done(function(x) {
startLive2dStuff(json);
})
.fail(function(x) { console.log("Failed ajax", x); })
.always(function(x) { hideProgress(container); });
}
});
}
function doesLive2dAnimationExist(artTabDiv, costume) {
var costumeId = costume;
if (costumeId[0] === '_') {
costumeId = costumeId.substr(1);
}
if (costume == "") {
costumeId = "costume0";
}
return $(artTabDiv).attr("data-live2d-exist-" + costumeId) == "true";
}
function hideLive2dView(artTabDiv) {
var live2dView = $(artTabDiv).find('.live2dstage');
live2dView.hide();
var stage = live2dView.data('live2dStage');
if (stage) {
stage.removeChildren();
}
refreshView(artTabDiv);
}
function createLive2dView(artTabDiv, callback) {
var live2dView = $(artTabDiv).find('.live2dstage');
if (live2dView.length < 1) {
mw.loader.using('ext.gadget.live2d').then( function () {
// Init renderer
const l2d_tdoll_renderer = PIXI.autoDetectRenderer(550, 550, {transparent: true});
const l2d_tdoll_stage = new PIXI.Container();
// Start the animation
function animate() {
requestAnimationFrame(animate);
l2d_tdoll_renderer.render(l2d_tdoll_stage);
}
animate();
var view = $(l2d_tdoll_renderer.view);
view.data('live2dStage', l2d_tdoll_stage);
view.addClass('live2dstage');
view.data('renderer', l2d_tdoll_renderer);
callback(view);
});
} else {
callback(live2dView.first());
}
}
function modelChanged(event, costumeSuffix) {
var artTabDiv = $(event.target).closest('.artTab');
var tdollId = artTabDiv.data('tdollId');
var artModelId = tdollId + costumeSuffix;
var variantswitcher = artTabDiv.find('.variantswitcher');
var costumeId = costumeSuffix;
if (costumeId[0] === '_') {
costumeId = costumeId.substr(1);
}
artTabDiv.attr('data-tdoll-costume', costumeId);
hideLive2dView(artTabDiv);
variantswitcher.find('.right').remove();
var basePath = mw.config.get("wgServer") + "/images/";
var cpp = window.gfUtils.createWikiPathPart;
var fullartPath = basePath + "thumb/" + cpp(artModelId +".png") + artModelId +".png/600px-" + artModelId +".png";
var fullartDamagedPath = basePath + "thumb/" + cpp(artModelId +"_D.png") + artModelId +"_D.png/600px-" + artModelId +"_D.png";
var normArt = artTabDiv.find(".fullart:not(.damaged) a");
normArt.attr("href", fullartPath );
normArt.find('img').attr("src", fullartPath );
normArt.find('img').attr("srcset", fullartPath );
normArt.find('img').attr("alt", fullartPath );
var damagedArt = artTabDiv.find(".fullart.damaged a");
damagedArt.attr("href", fullartDamagedPath );
damagedArt.find('img').attr("src", fullartDamagedPath );
damagedArt.find('img').attr("srcset", fullartDamagedPath );
damagedArt.find('img').attr("alt", fullartDamagedPath );
var isLive2dPossible = doesLive2dAnimationExist(artTabDiv, costumeSuffix);
if (isLive2dPossible) {
var live2dButton = $('<button></button>');
live2dButton.addClass('artTabLinks');
live2dButton.addClass('right');
live2dButton.click(function() {
if (live2dButton.is('.enabled')) {
hideLive2dView($(artTabDiv));
live2dButton.removeClass('enabled');
} else {
$(artTabDiv).addClass("loading");
createLive2dView(artTabDiv, function(view) {
view.show();
$(artTabDiv).find('.artTabContent').first().append(view);
refreshView(artTabDiv);
});
live2dButton.addClass('enabled');
}
});
live2dButton.text("Live2D");
variantswitcher.append(live2dButton);
}
refreshView(artTabDiv);
};
RLQ.push(function () {
$(document).ready(function() {
var artTabDiv = $('.artTab');
artTabDiv.on('costume_changed', modelChanged);
var buttonEventHandler = function(event) {
var currentElement = $(event.target);
var currentArtTab = currentElement.closest('.artTab');
var variant = currentElement.data('tdollVariant');
switchVariant(currentArtTab, variant);
};
var normalButton = $('<button></button>');
normalButton.addClass('artTabLinks');
normalButton.click(buttonEventHandler);
normalButton.attr('data-tdoll-variant', "");
normalButton.text("Normal");
var damagedButton = $('<button></button>');
damagedButton.addClass('artTabLinks');
damagedButton.click(buttonEventHandler);
damagedButton.attr('data-tdoll-variant', "D");
damagedButton.text("Damaged");
var variantswitcher = $('<div></div>');
variantswitcher.addClass('variantswitcher');
variantswitcher.append(normalButton);
variantswitcher.append(damagedButton);
artTabDiv.prepend(variantswitcher);
normalButton.click();
});
});