A pixel art painter geared specifically at NES pixel art. Includes export for .chr binary file as well as palette and namespace data.
您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

210 行
4.9KB

  1. import GlobalEvents from "/app/js/common/EventCaller.js";
  2. import Utils from "/app/js/common/Utils.js";
  3. import NESBank from "/app/js/models/NESBank.js";
  4. const BLI_TEMPLATE = "bank-list-item-template";
  5. const BLI_CANVAS = "bank-img";
  6. const BLI_TITLE = "title";
  7. const BLI_SELECTED = "list-item-selected";
  8. var Banks = {};
  9. var CurrentBank = "";
  10. function HANDLE_BankClick(e){
  11. var name = this.getAttribute("bankname");
  12. if (name !== CurrentBank){
  13. if (CurrentBank !== "")
  14. Banks[CurrentBank].el.classList.remove(BLI_SELECTED);
  15. CurrentBank = name;
  16. Banks[CurrentBank].el.classList.add(BLI_SELECTED);
  17. GlobalEvents.emit("change_surface", Banks[CurrentBank].bank);
  18. }
  19. }
  20. function SetElBankName(el, name){
  21. el.setAttribute("bankname", name);
  22. var sel = el.querySelector("." + BLI_TITLE);
  23. if (sel){
  24. sel.innerHTML = name;
  25. }
  26. }
  27. var RenderBankToEl = Utils.throttle(function(el, bank){
  28. var cnv = el.querySelector("." + BLI_CANVAS);
  29. var ctx = cnv.getContext("2d");
  30. var cw = Math.floor(cnv.clientWidth);
  31. var ch = Math.floor(cnv.clientHeight);
  32. if (cw <= 0 || ch <= 0){return;}
  33. var ctximg = ctx.getImageData(0,0,cw,ch);
  34. var idat = ctximg.data;
  35. for (let y=0; y < ch; y++){
  36. for (let x=0; x < cw; x++){
  37. var index = (y*(cw*4)) + (x*4);
  38. idat[index] = 0;
  39. idat[index+1] = 0;
  40. idat[index+2] = 0;
  41. idat[index+3] = 255;
  42. }
  43. }
  44. ctx.putImageData(ctximg, 0, 0);
  45. }, 500); // Only update twice a second.
  46. function HANDLE_BankDataChange(bank, e){
  47. RenderBankToEl(this, bank);
  48. }
  49. function ConnectElementToBank(el, bank){
  50. bank.listen("data_changed", HANDLE_BankDataChange.bind(el, bank));
  51. }
  52. function CreateBankDOMEntry(name, bank){
  53. var baseel = document.querySelector("." + BLI_TEMPLATE);
  54. if (!baseel){
  55. console.log("WARNING: Failed to find bank list item template.");
  56. return null;
  57. }
  58. var el = baseel.cloneNode(true);
  59. el.classList.remove(BLI_TEMPLATE);
  60. el.classList.remove("hidden");
  61. el.setAttribute("bankname", name);
  62. ConnectElementToBank(el, bank);
  63. SetElBankName(el, name);
  64. el.addEventListener("click", HANDLE_BankClick);
  65. baseel.parentNode.appendChild(el);
  66. setTimeout(()=>{
  67. RenderBankToEl(el, bank);
  68. }, 500); // Make the render call in about a half second. Allow DOM time to catch up?
  69. return el;
  70. }
  71. class CTRLBanksStore{
  72. constructor(){
  73. var HANDLE_ChangeSurface = function(surf){
  74. if (!(surf instanceof NESBank)){
  75. // TODO: Unselect any current bank element.
  76. CurrentBankIndex = "";
  77. } else {
  78. if (Banks.length <= 0 || (CurrentBank !== "" && Banks[CurrentBank].bank !== surf)){
  79. console.log("WARNING: Bank object being set outside of Bank Store.");
  80. }
  81. }
  82. }
  83. GlobalEvents.listen("change_surface", HANDLE_ChangeSurface);
  84. }
  85. get length(){
  86. return Object.keys(Banks).length;
  87. }
  88. get json(){
  89. var data = [];
  90. Object.keys(Banks).forEach((key) => {
  91. if (Banks.hasOwnProperty(key)){
  92. data.push({name:key, data:Banks[key].bank.base64});
  93. }
  94. });
  95. return JSON.stringify(data);
  96. }
  97. initialize(){
  98. if (this.length <= 0){
  99. this.createBank("Bank");
  100. }
  101. return this;
  102. }
  103. createBank(name, bbase64){
  104. if (!(name in Banks)){
  105. var bank = new NESBank();
  106. if (typeof(bbase64) === "string"){
  107. try {
  108. bank.base64 = bbase64;
  109. } catch (e) {
  110. console.log("Failed to create Bank. " + e.toString());
  111. bank = null;
  112. }
  113. }
  114. if (bank !== null){
  115. var el = CreateBankDOMEntry(name, bank);
  116. if (el){
  117. Banks[name] = {bank:bank, el:el};
  118. //Banks.push([name, bank, el]);
  119. if (this.length <= 1){
  120. Banks[name].el.click();
  121. //GlobalEvents.emit("change_surface", bank);
  122. }
  123. }
  124. }
  125. }
  126. return this;
  127. }
  128. removeBank(name){
  129. if (name in Banks){
  130. if (name === CurrentBank){
  131. var keys = Object.keys(Banks);
  132. if (keys.length > 1){
  133. CurrentBank = (keys[0] !== name) ? keys[0] : keys[1];
  134. } else {
  135. CurrentBank = "";
  136. }
  137. }
  138. Banks[name].el.parentNode.removeChild(Banks[name].el);
  139. delete Banks[name];
  140. if (CurrentBank !== ""){
  141. // TODO: Activate new Bank.
  142. }
  143. }
  144. return this;
  145. }
  146. renameBank(name, newname){
  147. if ((name in Banks) && !(newname in Banks)){
  148. Banks[newname] = Banks[name];
  149. delete Banks[name];
  150. SetElBankName(Banks[newname].el, newname);
  151. }
  152. return this;
  153. }
  154. activateBank(name){
  155. if (CurrentBank !== name && (name in Banks)){
  156. Banks[name].el.click();
  157. //CurrentBank = name;
  158. //GlobalEvents.emit("change_surface", Banks[CurrentBank].bank);
  159. }
  160. return this;
  161. }
  162. clear(){
  163. Object.keys(Banks).forEach((item) => {
  164. item.el.parentNode.removeChild(item.el);
  165. });
  166. Banks = {};
  167. if (CurrentBank !== "")
  168. GlobalEvents.emit("change_surface", null);
  169. CurrentBank = "";
  170. }
  171. }
  172. const instance = new CTRLBanksStore();
  173. export default instance;