bugfix250107.1
全站通知:

Widget:深网剧情流程图

来自恋与深空WIKI_BWIKI_哔哩哔哩
跳到导航 跳到搜索

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@logicflow/core/dist/style/index.css" /> <script src="https://cdn.jsdelivr.net/npm/@logicflow/core/dist/logic-flow.js"></script>

<link rel="stylesheet" href="/lysk/MediaWiki:CustomNode.css?action=raw&ctype=text/css" /> <script src="/lysk/index.php?title=MediaWiki:CustomNode.js&action=raw&ctype=text/javascript"></script>

<body>

</body> <script>

   const lf = new LogicFlow({
       container: document.querySelector("#container"),
       edgeType: 'plotNodeLine',
       stopZoomGraph: true,
       stopScrollGraph: true,
       //stopMoveGraph: true,
       adjustEdge: false,
       adjustEdgeStartAndEnd: false,
       adjustNodePosition: false,
       hideAnchors: true,
       nodeSelectedOutline: false,
       hoverOutline: false,
       nodeTextEdit: false,
       edgeTextEdit: false,
       textEdit: false,
   });
   lf.register(PlotNodeBox);
   lf.register(EndingNodeBox);
   lf.register(PlotNodeLine);
   lf.setTheme({
     arrow: {
       offset: 0,
       verticalLength: 0,
     },
     baseEdge: {
       stroke: "#f3f0c6",
       strokeWidth: 1,
     },
   });
   
   var graphData, graphModel, headImgURLArr;
   var currentNodeId;
   var nodesOrder = [];
   getFlowData();// 获取流程图json数据
   async function getFlowData() {
       graphData = await $.getJSON(`https://wiki.biligame.com/lysk/MediaWiki:.json?action=raw`);
       lf.render(graphData);  //渲染
       graphModel = lf.graphModel;
       console.log('流程数据:', graphData);
       if (graphData.nodes && graphData.nodes.length > 0) { initNodesOrder(); }
       console.log('节点顺序:', nodesOrder);
       initNodeBg();   // 处理节点背景图片
       initGraph();  //初始化画布
       initCurrentNode();  // 处理当前节点样式
       setTimeout(() => {
           jsonToHtml();
       }, 200)
   }
   // 初始化节点顺序
   function initNodesOrder() {
       nodesOrder.push(graphData.nodes[0].id);
       let curNode = graphData.nodes[0].id;
       while (lf.getNodeOutgoingNode(curNode).length > 0) {
           let nextNodes = lf.getNodeOutgoingNode(curNode);
           nodesOrder.push(nextNodes[0].id);
           curNode = nextNodes[0].id;
       }
   }
   // 左侧节点点击事件
   lf.on("node:click", (node) => {
       console.log('节点数据:', node.data);
       let oldNode = document.getElementById(currentNodeId);  //旧的当前节点
       if (oldNode) {
           changeNode(oldNode.id, node.data.id);
       }
   });
   // 更改currentNodeId值,右侧内容显示,分支节点按钮样式,屏幕滚动
   function changeNode(oldId, currentId) {
       if (oldId !== currentId) {
           $(`#${oldId}`)[0].classList.remove('active');
           currentNodeId = currentId;
           let currentNode = lf.getNodeDataById(currentId);
           changeCurrentNode();   // 当前节点样式
           // 点击分支节点切换右侧按钮显示
           if (currentNode.properties.isBranchNode) {
               activeButton = $(`.button-dialog[button-trigger=${currentNodeId}]`)[0];
               if (activeButton) { changeCurrentButton(activeButton); }
           }
       }
       if (!nodesOrder.includes(currentNodeId)) {
           changeDisplayContents();
       }
       scrollToContent();
   }
   // 按钮绑定点击事件
   function bindClickToButton() {
       const buttonArr = document.getElementsByClassName('button-dialog');
       for (let i = 0; i < buttonArr.length; i++) {
           buttonArr[i].addEventListener('click', function (event) {
               if (event.target.getAttribute('button-trigger')) {   //分支按钮
                   changeNode(currentNodeId, event.target.getAttribute('button-trigger'));
               }
               if (event.target.getAttribute('is-inline-button')) {   // 行内按钮
                   changeCurrentButton(event.target, true);
               }
           })
       }
   }


   // 初始化选中节点
   function initCurrentNode() {
       currentNodeId = graphData.nodes[0].id;
       changeCurrentNode();
   }
   function changeCurrentNode() {
       $(`#${currentNodeId}`)[0].classList.add('active');
   }
   // 加载所有节点内容Html
   var displayArr = [], tempDisplayArr = [];
   function jsonToHtml() {
       let htmlStr = ;
       for (let i = 0; i < graphData.nodes.length; i++) {
           let node = graphData.nodes[i];
           let contents = node.properties.dialogContents;
           let isInitNode = nodesOrder.includes(node.id);

htmlStr += `

`;
           if (contents && contents.length > 0) {   // 加载节点内容
               for (let i = 0; i < contents.length; i++) {
                   htmlStr += transNodeHtml(contents[i]);
               }
           }
htmlStr += '

';


       }
       const rightContainer = document.getElementsByClassName('plot-contents')[0];
       rightContainer.innerHTML = htmlStr;
       bindClickToButton();
   }
   // 点击后修改右侧内容显示
   function changeDisplayContents() {
       let node_content = $(`.${currentNodeId}_content`)[0];
       if (node_content.classList.contains('content-hidden')) {
           node_content.classList.remove('content-hidden');
       }
       // 向上向下分别寻找最近的显示节点
       let show_nodes = [currentNodeId];
       let up_id = lf.getNodeIncomingNode(currentNodeId)[0].id;
       while ($(`.${up_id}_content`)[0].classList.contains('content-hidden')) {
           $(`.${up_id}_content`)[0].classList.remove('content-hidden');  // 显示路径节点
           show_nodes.unshift(up_id);
           up_id = lf.getNodeIncomingNode(up_id)[0].id;
       }
       let down_nodes = lf.getNodeOutgoingNode(currentNodeId);
       let down_id = ;
       let find_down = false;
       while (down_nodes && down_nodes.length > 0) {  // 非叶子节点
           for (let i = 0; i < down_nodes.length; i++) {
               if (nodesOrder.includes(down_nodes[i].id)) {
                   down_id = down_nodes[i].id;
                   find_down = true;
                   break;
               }
           }
           if (find_down) {
               break;
           }
           else {
               down_id = down_nodes[0].id;
               $(`.${down_id}_content`)[0].classList.remove('content-hidden');  // 显示路径节点
               show_nodes.push(down_id);
               down_nodes = lf.getNodeOutgoingNode(down_id);
           }
       }
       let startIndex = nodesOrder.indexOf(up_id);
       let endIndex = nodesOrder.indexOf(down_id) === -1 ? nodesOrder.length : nodesOrder.indexOf(down_id);
       // 隐藏原中间节点
       let hidden_nodes = nodesOrder.slice(startIndex + 1, endIndex);
       for (let i = 0; i < hidden_nodes.length; i++) {
           $(`.${hidden_nodes[i]}_content`)[0].classList.add('content-hidden');
       }
       // 更新nodesOrder
       nodesOrder = nodesOrder.slice(0, startIndex + 1).concat(show_nodes).concat(nodesOrder.slice(endIndex));
       console.log(nodesOrder);
   }
 
   // 获取头像json数据
   $.getJSON('https://wiki.biligame.com/lysk/MediaWiki:HeadImg.json?action=raw')
     .then(response =>{ 
       //console.log('头像数据:',response);
       headImgURLArr = response;
     })
     .catch(error => {
       console.error('Error fetching data:', error);
     });
   var isFirstInlineContent = false;
   function transNodeHtml(unitObj) {
       let unitHtml = ;
       switch (unitObj.unitType) {
           case '头像在左':

unitHtml = `

<img src="${headImgURLArr[unitObj.headImg]}">
<img src="https://patchwiki.biligame.com/images/lysk/5/5a/awebfh9m8ntsq3dt06zml6xmnxt39iu.png"/>
${unitObj.roleName}
${unitObj.unitText}

`;

               break;
           case '头像在右':

unitHtml = `

<img src="${headImgURLArr[unitObj.headImg]}">
<img src="https://patchwiki.biligame.com/images/lysk/5/5a/awebfh9m8ntsq3dt06zml6xmnxt39iu.png"/>
${unitObj.roleName}
${unitObj.unitText}

`;

               break;
           case '旁白':

unitHtml = `

${unitObj.unitText}

`;

               break;
           case '按钮组':
               // 选中active规则:1.分支节点按钮根据当前节点是否在nodesOrder中选中;2.行内按钮根据是否为第一个行内按钮选中

unitHtml = `

`;
               for (let i = 0; i < unitObj.buttonsArr.length; i++) {
                   let button = unitObj.buttonsArr[i];
unitHtml += `
${button.buttonText}
`;
               }
unitHtml += '

';

               isFirstInlineContent = true;
               break;
           case '节点内选项内容':

unitHtml = `

`;
               for (let i = 0; i < unitObj.dialogContents.length; i++) {
                   unitHtml += transNodeHtml(unitObj.dialogContents[i]);
               }
unitHtml += '

';

               isFirstInlineContent = false;
               break;
       }
       return unitHtml;
   }


   // 滚动到当前节点位置
   function scrollToContent() {
       const container = $('.plot-contents')[0];
       const targetElement = $(`.${currentNodeId}_content`)[0];
       if (targetElement.offsetTop == 0) { return; }
       const targetOffsetTop = targetElement.offsetTop >= 30 ? targetElement.offsetTop - 30 : targetElement.offsetTop;
       // 将容器滚动到目标元素的位置
       container.scrollTo({
           top: targetOffsetTop,
           behavior: 'smooth'
       });
   }
   // 修改同级button样式 & 分支节点替换显示
   function changeCurrentButton(target, isInlineButton) {
       let buttonArr = target.parentElement.children;
       for (let i = 0; i < buttonArr.length; i++) {
           if (buttonArr[i].classList.contains('active')) {
               buttonArr[i].classList.remove('active');
           }
       }
       target.classList.add('active');
       if (isInlineButton) {  // 显示节点内选项内容
           let sibling = target.parentElement.nextElementSibling;
           while (sibling && sibling.className === "inline-button-contents") {
               if (sibling.getAttribute('button-belong') === target.innerText) {
                   sibling.style.display = ;
               }
               else { sibling.style.display = 'none'; }
               sibling = sibling.nextElementSibling;
           }
           return;
       }
   }


   // 初始化节点背景:根据同级节点个数
   function initNodeBg() {
       const plotNodes = $('.plot-node');
       for (let i = 0; i < plotNodes.length; i++) {
           let isBranch = lf.getNodeDataById(plotNodes[i].id).properties.isBranchNode;
           if (isBranch) {  // 分支节点
               plotNodes[i].style.backgroundImage = "url(https://patchwiki.biligame.com/images/lysk/9/94/0ueh2qo77mprdnyxylj8fc20cdh09gz.png)"
           }
           else {
               plotNodes[i].style.backgroundImage = "url(https://patchwiki.biligame.com/images/lysk/4/43/hx0pjs2s9mgh7egmn7bbla00nmv8uvx.png)"
           }
       }
   }
   function initGraph() {
       // 计算画布偏移和画布宽高
       let minY = graphData.nodes[0].y, maxY = graphData.nodes[0].y;
       for (let i = 0; i < graphData.nodes.length; i++) {
           let node = graphData.nodes[i];
           minY = node.y < minY ? node.y : minY;
           maxY = node.y > maxY ? node.y : maxY;
       }
       let conWidth = $('#container')[0].clientWidth === 0 ? window.innerWidth * 0.9 : $('#container')[0].clientWidth;
       let transX = conWidth / 2 - graphData.nodes[0].x;
       let transY = 50 - minY;
       lf.translate(transX, transY);
       let height = maxY - minY + 100;
       $('#container>div')[0].style.height = height + 'px';
       lf.updateEditConfig({ stopMoveGraph: true, });
   }
 
 
 
   // tab切换绑定
   addTabListener();
   function addTabListener(){
     let tabList = $('.SwitchContainer>.BtnContainer>.btn');
     for(let i = 0; i < tabList.length; i++){
       tabList[i].addEventListener('click', function(event){
         if(event.target.innerText === '事件回想'){
           $('.right-container>.plot-contents')[0].style.display = 'block';
           $('.right-container>.accident-contents')[0].style.display = 'none';
         }
         else{
           $('.right-container>.plot-contents')[0].style.display = 'none';
           $('.right-container>.accident-contents')[0].style.display = 'block';
           bindClickToButton();
         }
       })
     }
   }
   
   var accidentData;
   getAccidentData();// 获取偶发事件json数据
   async function getAccidentData(){
     accidentData = await $.getJSON(`https://wiki.biligame.com/lysk/MediaWiki:.json?action=raw`);
     console.log('事件数据:', accidentData);
     setTimeout(()=>{
       // 写入HTML
       let accidentListHtml = "", accidentContentsHtml = ""; 
       let accList = Object.keys(accidentData);
       for(let i = 0; i < accList.length; i++){ //transNodeHtml
         let accContent = "";

accidentListHtml += `

${accList[i]}

`;

         for(let j = 0; j < accidentData[accList[i]].length; j++){
           accContent += transNodeHtml(accidentData[accList[i]][j]);
         }

accidentContentsHtml += `

`;

       }
       $('.accident-list')[0].innerHTML = accidentListHtml;
       $('.accident-contents')[0].innerHTML = accidentContentsHtml;
       // 处理默认选中
       $('.accident-list>.accident-module')[0].classList.add('active');
       $('.accident-contents>.accident-content')[0].style.display = "block";
       // 绑定点击事件
       let oldAccDiv = $('.accident-list>.accident-module')[0];
       let accDivList = $('.accident-list>.accident-module');
       for(let i = 0;i < accDivList.length; i++){
         accDivList[i].addEventListener("click", function(event){
           oldAccDiv.classList.remove('active');
           event.target.classList.add('active');
           $(`.accident-content[data-param="${oldAccDiv.innerText}"]`)[0].style.display = "none";
           $(`.accident-content[data-param="${event.target.innerText}"]`)[0].style.display = "block";
           oldAccDiv = event.target;
         })
       }
     },500)
   }
   
   // 手机端目录显示
   setTimeout(()=>{
     if($('.side-directory').length > 0) {
       bindDir();
       handleScroll();
     }
   },1000)
   function bindDir(){
    $('.side-directory')[0].addEventListener('click',function(){

    $('.left-container>.SwitchContainer>.BtnContent')[0].style.display = "block";    });

    $('.left-container>.SwitchContainer>.BtnContent')[0].addEventListener('click',function(event){
      if(event.target.classList.contains('BtnContent')){
        $('.left-container>.SwitchContainer>.BtnContent')[0].style.display = "none";
      }
    })
  }
  //禁止底部滑动
 function handleScroll(){
   var overlay = $('#container.plotScrollbar')[0];
   // 监听鼠标滚轮事件
   overlay.addEventListener('wheel', function(event) {
       if ((event.deltaY > 0 && (overlay.scrollTop + overlay.clientHeight >= overlay.scrollHeight)) || 
           (event.deltaY < 0 && overlay.scrollTop <= 0)) {
           event.preventDefault();
       }
   }, { passive: false });
   // 监听触摸开始事件
   overlay.addEventListener('touchstart', function(event) {
       var initialTouch = event.touches[0];
       var initialScrollTop = overlay.scrollTop;
       // 记录初始触摸位置和滚动位置
       overlay.dataset.initialTouchY = initialTouch.clientY;
       overlay.dataset.initialScrollTop = initialScrollTop;
   });
   // 监听触摸移动事件
   overlay.addEventListener('touchmove', function(event) {
       var currentTouch = event.touches[0];
       var initialTouchY = parseFloat(overlay.dataset.initialTouchY);
       var initialScrollTop = parseFloat(overlay.dataset.initialScrollTop);
       // 计算触摸移动的距离
       var deltaY = currentTouch.clientY - initialTouchY;
       // 检查是否已经滚动到顶部或底部
       if ((deltaY < 0 && (initialScrollTop + overlay.clientHeight >= overlay.scrollHeight)) || 
           (deltaY > 0 && initialScrollTop <= 0)) {
           // 如果滚动到底部或顶部,则阻止默认滚动行为
           event.preventDefault();
       }
   }, { passive: false });
 }

</script>

<style>

  1. container {
 width: 100%;
 height: 100%;
 overflow: auto;

}

  1. container>div{
 min-height: 100%;
 min-width: 370px;

} .lf-graph{

 background: transparent;

} text.lf-element-text {

   font-size: 14px;
   color: #cfebf9;
   font-family: 'SimSun', sans-serif;
   font-weight: bold;
   cursor: pointer;

}


.left-dialog {

   display: flex;
   flex-direction: row;
   gap: 5px;
   margin-bottom: 15px;
   padding: 5px;
   position: relative;

}

.accident-module:hover {

   text-shadow: 1px 0px 3px #e3f7ffad;

} .accident-module {

   width: 100%;
   height: 50px;
   color: #d4ebf4;
   background-image: url(https://patchwiki.biligame.com/images/lysk/7/77/f4cmrvlnryz0mj5vvi72tbu7ah7q943.png);
   background-size: 100% 100%;
   text-align: center;
   font-size: 16px;
   line-height: 47px;
   position: relative;
   cursor: pointer;

} .accident-module.active::before {

   content: "";
   position: absolute;
   width: 100%;
   height: 100%;
   top: 0;
   left: 0;
   background-image: url(https://patchwiki.biligame.com/images/lysk/1/19/fgcojpmsabediig497zgx1oxl7w1183.png);
   background-size: 100% 95%;
   background-repeat: no-repeat;

}

 .content-hidden{
   display: none;
 }

.right-dialog {

   display: flex;
   flex-direction: row-reverse;
   gap: 5px;    
   margin-bottom: 15px;
   padding: 5px;
   position: relative;

} .voiceover-dialog{

   margin-bottom: 10px;
   color: #6887a7;

}

.buttons-group {

   display: flex;
   flex-direction: column;
   align-items: center;

} .button-dialog{

   min-width: 50%;
   height: 50px;
   padding: 10px;
   text-align: center;
   cursor: pointer;
   box-sizing: border-box;
   margin-bottom: 10px;
   background-image: url(https://patchwiki.biligame.com/images/lysk/5/5a/dlw994nn9ke41wnre15utjrttdlwldm.png);
   color: #cfebf9;
   background-size: 100% 100%;

} .button-dialog.active {

   font-weight: bold;

} .button-dialog:not(.active):hover {

   color: #9fbac6;

}

.head-img {

   height: 70px;
   width: 70px;
   display: flex;
   align-items: flex-end;
   background-image: url(https://patchwiki.biligame.com/images/lysk/7/70/s3kgqr8k04kftquzm947mbjq2fppq3s.png);
   background-size: 100% 100%;
   padding: 5px;

}

.left-dialog>img {

   position: absolute;
   width: 65px;
   left: 10px;
   bottom: -5px;

}

.text-container {

   flex: 1; 

}

.right-dialog > .text-container {

   text-align: right;

} .right-dialog>img {

   position: absolute;
   width: 65px;
   right: 7px;
   bottom: -5px;

}

.role-name {

   color: #a1c6ea;
   font-size: 15px;
   font-weight: bold;
   position: relative;

} .role-name::before {

   content: "";
   width: 100%;
   height: 100%;
   left: 0;
   position: absolute;
   background-image: url(https://patchwiki.biligame.com/images/lysk/d/d8/cikfxl6ub7j5m6fj04sgwa6m9cm7xee.png);
   background-repeat: no-repeat;
   padding-left: 15px;
   background-size: auto 75%;
   background-position-y: 5px;

} .left-dialog .role-name {

   padding-left: 15px;

} .right-dialog .role-name {

   padding-right: 15px;

} .right-dialog .role-name::before {

   transform: scaleX(-1);

}

.unit-text {

   color: #d1e6f1;
   padding: 5px 10px;
   height: 100%;
   background: url(https://patchwiki.biligame.com/images/lysk/d/d7/2f97ck87p9vwsrwyc6a4q0x5ye1i1bv.png);
   background-size: 70%;
   background-repeat: no-repeat;
   text-align: left;

} .right-dialog .unit-text {

   margin-left: 70px;

} </style>