Widget:推荐阅读
<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;
getFlowData();// 获取流程图json数据
async function getFlowData(){
graphData = await $.getJSON(`https://wiki.biligame.com/lysk/MediaWiki:.json?action=raw`);
lf.render(graphData); //渲染
graphModel = lf.graphModel;
if (graphData.nodes && graphData.nodes.length > 0) { initNodesOrder(); }
console.log('流程数据:', graphData);
console.log('节点顺序:', nodesOrder);
initNodeBg(); // 处理节点背景图片
initCurrentNode(); // 处理当前节点样式
initGraph(); //初始化画布
}
// 计算节点顺序
function initNodesOrder() {
nodesOrder = [graphData.nodes[0].id];
for (let i = 0; i < nodesOrder.length; i++) {
let nextOrder;
if (typeof nodesOrder[i] == 'string') {
nextOrder = setNodeOrder(nodesOrder[i]);
}
else {
for (let j = 0; j < nodesOrder[i].length; j++) {
let tempOrder = setNodeOrder(nodesOrder[i][j]);
if (tempOrder) {
nextOrder = tempOrder;
break;
}
}
}
if (nextOrder) {
nodesOrder.push(nextOrder);
}
}
}
// 下一层节点顺序
function setNodeOrder(nodeId) {
if (lf.getNodeDataById(nodeId).type === 'ending-node') {
return;
}
let nextNodes = graphModel.getNodeOutgoingNode(nodeId);
if (nextNodes.length === 0) { return; }
if (nextNodes.length === 1) {
if (!nodesOrder.includes(nextNodes[0].id)) {
return nextNodes[0].id;
}
else {
return;
}
}
else if (nextNodes.length === 2) {
return [findLeftNodeId(nodeId), findRightNodeId(nodeId)];
}
else {
let leftId = findLeftNodeId(nodeId), rightId = findRightNodeId(nodeId);
return [leftId,
nextNodes.filter(node => {
return node.id !== leftId && node.id !== rightId;
})[0].id,
rightId];
}
}
// 寻找同级最左端的节点
function findLeftNodeId(parentId) {
const nextNodes = graphModel.getNodeOutgoingNode(parentId);
let leftId = nextNodes[0].id;
let minX = nextNodes[0].x;
for (let i = 1; i < nextNodes.length; i++) {
if (minX > nextNodes[i].x) {
minX = nextNodes[i].x;
leftId = nextNodes[i].id;
}
}
return leftId;
}
// 寻找同级最右端的节点
function findRightNodeId(parentId) {
const nextNodes = graphModel.getNodeOutgoingNode(parentId);
let rigthId = nextNodes[0].id;
let maxX = nextNodes[0].x;
for (let i = 1; i < nextNodes.length; i++) {
if (maxX < nextNodes[i].x) {
maxX = nextNodes[i].x;
rigthId = nextNodes[i].id;
}
}
return rigthId;
}
// 获取头像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);
});
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, });
}
// 初始化节点背景:根据同级节点个数
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 initCurrentNode(){
currentNodeId = graphData.nodes[0].id;
changeCurrentNode();
setTimeout(()=>{
jsonToHtml();
},200)
}
function changeCurrentNode() {
$(`#${currentNodeId}`)[0].classList.add('active');
}
// 加载所有节点内容Html
var displayArr = [], tempDisplayArr = [];
function jsonToHtml() {
let htmlStr = ;
let parentFlag = ;
let htmlOrders = [];
for (let i = 0; i < nodesOrder.length; i++) {
if (typeof nodesOrder[i] === 'string') {
htmlOrders.push(nodesOrder[i]);
}
else {
for (let j = 0; j < nodesOrder[i].length; j++) {
htmlOrders.push(nodesOrder[i][j]);
}
}
}
for (let i = 0; i < htmlOrders.length; i++) {
let node = lf.getNodeDataById(htmlOrders[i]);
let isBranch = node.properties.isBranchNode; // 是否为分支节点
let contents = node.properties.dialogContents;
let nodeHtml = `
if (!isBranch) {
displayArr.push(node.id);
}
if (isBranch && node.properties.parentNode != parentFlag) { //加载第一个分支节点
parentFlag = node.properties.parentNode;
displayArr.push(node.id);
}
if (contents && contents.length > 0) { // 加载节点内容
for (let i = 0; i < contents.length; i++) {
nodeHtml += transNodeHtml(contents[i]);
}
}
htmlStr += nodeHtml + '';
}
const rightContainer = document.getElementsByClassName('plot-contents')[0];
rightContainer.innerHTML = htmlStr;
bindClickToButton();
tempDisplayArr = JSON.parse(JSON.stringify(displayArr));
}
var isFirstInlineContent = false;
function transNodeHtml(unitObj) {
let unitHtml = ;
switch (unitObj.unitType) {
case '头像在左':
unitHtml = `
`;
break;
case '头像在右':
unitHtml = `
`;
break;
case '旁白':
unitHtml = `
`;
break;
case '按钮组':
unitHtml = `
';
isFirstInlineContent = true;
break;
case '节点内选项内容':
unitHtml = `
';
isFirstInlineContent = false;
break;
}
return unitHtml;
}
// 左侧节点点击事件
lf.on("node:click", (node) => {
console.log('节点数据:', node.data);
let oldNode = document.getElementById(currentNodeId); //旧的当前节点
if (oldNode) $(`#${currentNodeId}`)[0].classList.remove('active');
currentNodeId = node.data.id;
changeCurrentNode(); // 当前节点样式
displayArr = JSON.parse(JSON.stringify(tempDisplayArr));
// 点击分支节点切换显示
if (node.data.properties.isBranchNode) {
activeButton = $(`.button-dialog[button-trigger=${currentNodeId}]`)[0];
if (activeButton) { changeCurrentButton(activeButton); }
}
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')) { //分支按钮
// 修改左侧节点样式
let oldNode = document.getElementById(currentNodeId);
$(`#${currentNodeId}`)[0].classList.remove('active');
currentNodeId = event.target.getAttribute('button-trigger');
changeCurrentNode();
}
displayArr = JSON.parse(JSON.stringify(tempDisplayArr));
changeCurrentButton(event.target, event.target.getAttribute('is-inline-button') ? true : false);
changeDisplayContents();
})
}
}
// 修改同级button样式 & 分支节点替换显示
function changeCurrentButton(target, isInlineButton) {
let buttonArr = target.parentElement.children;
for (let i = 0; i < buttonArr.length; i++) {
//if (isInlineButton) { // 隐藏节点内选项内容
//$(`.inline-button-contents[button-belong=${buttonArr[i].innerText}]`)[0].style.display = 'none';
//}
// else { // 隐藏节点内容
// document.getElementsByClassName(buttonArr[i].getAttribute('button-trigger') + '_content')[0].style.display = 'none';
// }
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;
}
// 隐藏结局节点之后的所有内容
let node = lf.getNodeDataById(target.getAttribute('button-trigger'));
if (node && node.type === 'ending_node') {
let index = displayArr.indexOf(graphModel.getNodeIncomingNode(currentNodeId)[0].id);
if (index !== -1) {
displayArr.splice(index + 1, 1, currentNodeId);
displayArr = displayArr.slice(0, index + 2);
}
}
else {
// 替换displayArr
const index = displayArr.indexOf(graphModel.getNodeIncomingNode(currentNodeId)[0].id);
if (index !== -1) {
displayArr.splice(index + 1, 1, currentNodeId);
tempDisplayArr = JSON.parse(JSON.stringify(displayArr));
}
}
}
// 遍历content
function changeDisplayContents() {
let contentArr = $('.plot-contents>div');
for (let i = 0; i < contentArr.length; i++) {
let content = contentArr[i];
if (displayArr.indexOf(content.className.split('_content')[0]) !== -1) {
content.style.display = ;
}
else {
content.style.display = 'none';
}
}
}
// 滚动到当前节点位置
function scrollToContent() {
const container = $('.plot-contents')[0];
const targetElement = $(`.${currentNodeId}_content`)[0];
if(targetElement.offsetTop == 0){ return; }
const targetOffsetTop = targetElement.offsetTop >= 10 ? targetElement.offsetTop - 10 : targetElement.offsetTop;
// 将容器滚动到目标元素的位置
container.scrollTo({
top: targetOffsetTop,
behavior: 'smooth'
});
}
// 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 += `
`;
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>
- container {
width: 100%; height: 100%; overflow: auto;
}
- 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;
}
.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;
} .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;
}
.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;
}
.content_hidden {
display: none;
}
.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>

沪公网安备 31011002002714 号