parent
fdef8ce4d4
commit
1a5a34d289
18 changed files with 2051 additions and 5 deletions
@ -0,0 +1,267 @@ |
||||
{ |
||||
"nodes": [ |
||||
{ "id": "node1", "label": "计算机", "color": "#4682B4", "classID": 0 }, |
||||
{ |
||||
"id": "node2", |
||||
"label": "前端", |
||||
"color": "rgba(254, 241, 0, 1)", |
||||
"classID": 1 |
||||
}, |
||||
{ |
||||
"id": "node3", |
||||
"label": "js", |
||||
"color": "rgba(239, 242, 18, 1)", |
||||
"classID": 2 |
||||
}, |
||||
{ |
||||
"id": "node4", |
||||
"label": "html", |
||||
"color": "rgba(230, 234, 10, 1)", |
||||
"classID": 3 |
||||
}, |
||||
{ |
||||
"id": "node5", |
||||
"label": "css", |
||||
"color": "rgba(244, 231, 0, 1)", |
||||
"classID": 4 |
||||
}, |
||||
{ |
||||
"id": "node6", |
||||
"label": "less", |
||||
"color": "rgba(15, 245, 57, 1)", |
||||
"classID": 5 |
||||
}, |
||||
{ |
||||
"id": "node7", |
||||
"label": "scss", |
||||
"color": "rgba(133, 255, 11, 1)", |
||||
"classID": 6 |
||||
}, |
||||
{ |
||||
"id": "node8", |
||||
"label": "VUE", |
||||
"color": "rgba(42, 255, 0, 1)", |
||||
"classID": 7 |
||||
}, |
||||
{ |
||||
"id": "node9", |
||||
"label": "React", |
||||
"color": "rgba(76, 73, 245, 1)", |
||||
"classID": 8 |
||||
}, |
||||
{ "id": "node10", "label": "模块化", "color": "#4682B4", "classID": 9 }, |
||||
{ "id": "node11", "label": "webpack", "color": "#4682B4", "classID": 10 }, |
||||
{ "id": "node12", "label": "vite", "color": "#4682B4", "classID": 11 }, |
||||
{ |
||||
"id": "node13", |
||||
"label": "uniapp", |
||||
"color": "rgba(77, 255, 0, 1)", |
||||
"classID": 12 |
||||
}, |
||||
{ |
||||
"id": "node14", |
||||
"label": "element", |
||||
"color": "rgba(33, 162, 255, 1)", |
||||
"classID": 13 |
||||
}, |
||||
{ |
||||
"id": "node15", |
||||
"label": "web3", |
||||
"color": "rgba(255, 0, 251, 1)", |
||||
"classID": 14 |
||||
}, |
||||
{ |
||||
"id": "node16", |
||||
"label": "webGl", |
||||
"color": "rgba(208, 0, 249, 1)", |
||||
"classID": 15 |
||||
}, |
||||
{ |
||||
"id": "node17", |
||||
"label": "three", |
||||
"color": "rgba(225, 0, 255, 1)", |
||||
"classID": 16 |
||||
}, |
||||
{ |
||||
"id": "node18", |
||||
"label": "后端", |
||||
"color": "rgba(0, 229, 255, 1)", |
||||
"classID": 17 |
||||
}, |
||||
{ |
||||
"id": "node19", |
||||
"label": "java", |
||||
"color": "rgba(237, 229, 85, 1)", |
||||
"classID": 18 |
||||
}, |
||||
{ |
||||
"id": "node20", |
||||
"label": "PHP", |
||||
"color": "rgba(195, 206, 215, 1)", |
||||
"classID": 19 |
||||
}, |
||||
{ |
||||
"id": "node21", |
||||
"label": "Go", |
||||
"color": "rgba(255, 0, 0, 1)", |
||||
"classID": 20 |
||||
}, |
||||
{ |
||||
"id": "node22", |
||||
"label": "Python", |
||||
"color": "rgba(109, 238, 180, 1)", |
||||
"classID": 21 |
||||
}, |
||||
{ "id": "node23", "label": "MySQL", "color": "#4682B4", "classID": 22 }, |
||||
{ |
||||
"id": "node24", |
||||
"label": "人工智能", |
||||
"color": "rgba(180, 5, 255, 1)", |
||||
"classID": 23 |
||||
}, |
||||
{ |
||||
"id": "node25", |
||||
"label": "python", |
||||
"color": "rgba(255, 8, 8, 1)", |
||||
"classID": 24 |
||||
}, |
||||
{ |
||||
"id": "node26", |
||||
"label": "AI模型", |
||||
"color": "rgba(10, 138, 244, 1)", |
||||
"classID": 25 |
||||
}, |
||||
{ |
||||
"id": "node27", |
||||
"label": "Spring Framework", |
||||
"color": "rgba(242, 238, 14, 1)", |
||||
"classID": 26 |
||||
}, |
||||
{ |
||||
"id": "node28", |
||||
"label": "Hibernate", |
||||
"color": "rgba(242, 238, 14, 1)", |
||||
"classID": 27 |
||||
}, |
||||
{ |
||||
"id": "node29", |
||||
"label": "Spring MVC", |
||||
"color": "rgba(242, 238, 14, 1)", |
||||
"classID": 28 |
||||
}, |
||||
{ |
||||
"id": "node30", |
||||
"label": "Gin", |
||||
"color": "rgba(255, 0, 0, 1)", |
||||
"classID": 29 |
||||
}, |
||||
{ |
||||
"id": "node31", |
||||
"label": "Echo", |
||||
"color": "rgba(255, 0, 0, 1)", |
||||
"classID": 30 |
||||
}, |
||||
{ |
||||
"id": "node32", |
||||
"label": "Beego", |
||||
"color": "rgba(255, 8, 0, 1)", |
||||
"classID": 31 |
||||
}, |
||||
{ |
||||
"id": "node33", |
||||
"label": "Laravel", |
||||
"color": "rgba(200, 209, 217, 1)", |
||||
"classID": 32 |
||||
}, |
||||
{ |
||||
"id": "node34", |
||||
"label": "Symfony", |
||||
"color": "rgba(182, 194, 204, 1)", |
||||
"classID": 33 |
||||
}, |
||||
{ |
||||
"id": "node35", |
||||
"label": "CodeIgniter", |
||||
"color": "rgba(188, 197, 204, 1)", |
||||
"classID": 34 |
||||
}, |
||||
{ |
||||
"id": "node36", |
||||
"label": "Django", |
||||
"color": "rgba(36, 245, 144, 1)", |
||||
"classID": 35 |
||||
}, |
||||
{ |
||||
"id": "node37", |
||||
"label": "Flask", |
||||
"color": "rgba(41, 244, 176, 1)", |
||||
"classID": 36 |
||||
}, |
||||
{ |
||||
"id": "node38", |
||||
"label": "FastAPI", |
||||
"color": "rgba(58, 244, 142, 1)", |
||||
"classID": 37 |
||||
} |
||||
], |
||||
"links": [ |
||||
{ "source": "node2", "target": "node3", "label": "", "classID": 0 }, |
||||
{ "source": "node2", "target": "node5", "label": "", "classID": 1 }, |
||||
{ "source": "node2", "target": "node4", "label": "", "classID": 2 }, |
||||
{ "source": "node1", "target": "node2", "label": "前端方向", "classID": 3 }, |
||||
{ "source": "node5", "target": "node6", "label": "", "classID": 4 }, |
||||
{ "source": "node5", "target": "node7", "label": "", "classID": 5 }, |
||||
{ "source": "node3", "target": "node8", "label": "", "classID": 6 }, |
||||
{ "source": "node3", "target": "node9", "label": "", "classID": 7 }, |
||||
{ "source": "node3", "target": "node10", "label": "", "classID": 8 }, |
||||
{ "source": "node10", "target": "node11", "label": "", "classID": 9 }, |
||||
{ "source": "node10", "target": "node12", "label": "", "classID": 10 }, |
||||
{ "source": "node11", "target": "node9", "label": "", "classID": 11 }, |
||||
{ "source": "node11", "target": "node8", "label": "", "classID": 12 }, |
||||
{ "source": "node12", "target": "node8", "label": "", "classID": 13 }, |
||||
{ "source": "node8", "target": "node13", "label": "", "classID": 14 }, |
||||
{ "source": "node8", "target": "node14", "label": "", "classID": 15 }, |
||||
{ "source": "node11", "target": "node13", "label": "", "classID": 16 }, |
||||
{ "source": "node12", "target": "node13", "label": "", "classID": 17 }, |
||||
{ "source": "node2", "target": "node15", "label": "", "classID": 18 }, |
||||
{ "source": "node15", "target": "node16", "label": "", "classID": 19 }, |
||||
{ "source": "node16", "target": "node17", "label": "", "classID": 20 }, |
||||
{ "source": "node1", "target": "node1", "label": "", "classID": 21 }, |
||||
{ |
||||
"source": "node1", |
||||
"target": "node18", |
||||
"label": "后端方向", |
||||
"classID": 22 |
||||
}, |
||||
{ "source": "node18", "target": "node21", "label": "", "classID": 23 }, |
||||
{ "source": "node18", "target": "node20", "label": "", "classID": 24 }, |
||||
{ "source": "node18", "target": "node19", "label": "", "classID": 25 }, |
||||
{ "source": "node18", "target": "node22", "label": "", "classID": 26 }, |
||||
{ "source": "node22", "target": "node23", "label": "", "classID": 27 }, |
||||
{ "source": "node19", "target": "node23", "label": "", "classID": 28 }, |
||||
{ "source": "node20", "target": "node23", "label": "", "classID": 29 }, |
||||
{ "source": "node21", "target": "node23", "label": "", "classID": 30 }, |
||||
{ |
||||
"source": "node1", |
||||
"target": "node24", |
||||
"label": "人工智能方向", |
||||
"classID": 31 |
||||
}, |
||||
{ "source": "node24", "target": "node25", "label": "", "classID": 32 }, |
||||
{ "source": "node24", "target": "node26", "label": "", "classID": 33 }, |
||||
{ "source": "node12", "target": "node9", "label": "", "classID": 34 }, |
||||
{ "source": "node19", "target": "node27", "label": "", "classID": 35 }, |
||||
{ "source": "node19", "target": "node28", "label": "", "classID": 36 }, |
||||
{ "source": "node19", "target": "node29", "label": "", "classID": 37 }, |
||||
{ "source": "node21", "target": "node30", "label": "", "classID": 38 }, |
||||
{ "source": "node21", "target": "node31", "label": "", "classID": 39 }, |
||||
{ "source": "node21", "target": "node32", "label": "", "classID": 40 }, |
||||
{ "source": "node20", "target": "node33", "label": "", "classID": 41 }, |
||||
{ "source": "node20", "target": "node34", "label": "", "classID": 42 }, |
||||
{ "source": "node20", "target": "node35", "label": "", "classID": 43 }, |
||||
{ "source": "node22", "target": "node36", "label": "", "classID": 44 }, |
||||
{ "source": "node22", "target": "node37", "label": "", "classID": 45 }, |
||||
{ "source": "node22", "target": "node38", "label": "", "classID": 46 } |
||||
] |
||||
} |
||||
|
@ -0,0 +1,29 @@ |
||||
/** |
||||
* 获取计算机导论信息 |
||||
* @returns |
||||
*/ |
||||
export function getLearnInfoApi() { |
||||
return new Promise((resolve) => { |
||||
const data = [ |
||||
{ name: '总学时', value: '36学时' }, |
||||
{ name: '已分配学时', value: '20学时' }, |
||||
{ name: '总学分', value: '2.0分' }, |
||||
{ name: '章节数', value: '10章' }, |
||||
{ name: '未分配学时', value: '' }, |
||||
{ name: '知识点总数', value: '33个' }, |
||||
] |
||||
setTimeout(() => resolve(data), 500 * Math.random()) |
||||
}) |
||||
} |
||||
/** |
||||
* 获取计算机导论信息 |
||||
* @returns |
||||
*/ |
||||
export function getOrogramObjectiveApi() { |
||||
return new Promise((resolve) => { |
||||
const data = { |
||||
con: '内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容', |
||||
} |
||||
setTimeout(() => resolve(data), 500 * Math.random()) |
||||
}) |
||||
} |
After Width: | Height: | Size: 322 B |
After Width: | Height: | Size: 510 B |
@ -0,0 +1,153 @@ |
||||
<template> |
||||
<div id="3d-graph"></div> |
||||
</template> |
||||
|
||||
<script lang="ts" setup> |
||||
// import { useRouter } from 'vue-router' |
||||
import { onMounted, ref,reactive,watch } from 'vue' |
||||
import ForceGraph3D from '3d-force-graph' |
||||
//@ts-ignore |
||||
import { CSS2DRenderer, CSS2DObject,} from 'three/examples/jsm/renderers/CSS2DRenderer.js' |
||||
//@ts-ignore |
||||
import SpriteText from '../spritetext.js' |
||||
// const $router = useRouter() |
||||
// const jsonData = ref(null) |
||||
let Graph:any = reactive({}) |
||||
const props = defineProps({ |
||||
width:{ |
||||
type:Number, |
||||
default:window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth, |
||||
}, |
||||
height:{ |
||||
type:Number, |
||||
default:window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight, |
||||
} |
||||
}) |
||||
watch(() => props.width, ne => Graph.width(ne)) |
||||
watch(() => props.height, ne => Graph.height(ne)) |
||||
const dom = ref(null) |
||||
onMounted(() => { |
||||
|
||||
|
||||
Graph = ForceGraph3D({ |
||||
extraRenderers: [new CSS2DRenderer()], |
||||
})(document.getElementById('3d-graph') as HTMLElement) |
||||
.jsonUrl('../../../public/data.json') |
||||
// .nodeAutoColorBy('group') |
||||
.nodeThreeObject((node: any) => { |
||||
const nodeEl = document.createElement('div') |
||||
nodeEl.textContent = node.label |
||||
nodeEl.style.color = '#333333' |
||||
nodeEl.style.borderRadius = '50%' |
||||
// console.log(node, 111, Graph.graphData().nodes) |
||||
return new CSS2DObject(nodeEl) |
||||
}) |
||||
.linkLabel((link:any) => link.label) // 指定链接上显示的文字属性 |
||||
.linkWidth(0.8) |
||||
.linkHoverPrecision(0.5) // 设置链接文字的显示精度,这里设置为一个足够小的值 |
||||
.linkColor(() => '#dd92fd') // 设置连接线颜色为红色 |
||||
.backgroundColor('#f5f6fd') |
||||
.width(props.width) |
||||
.height(props.height) |
||||
.linkThreeObjectExtend(true) |
||||
.nodeColor((node: any) => { |
||||
return node.color |
||||
}) |
||||
.nodeRelSize(7) // 设置节点的相对大小为4 |
||||
.nodeResolution(20) |
||||
.linkDirectionalArrowLength(3) // 设置连接线箭头长度为3 |
||||
.linkDirectionalArrowRelPos(1) // 设置连接线箭头位置为连接线末端 |
||||
.nodeThreeObjectExtend(true) |
||||
.onNodeClick((node: any) => { |
||||
// Aim at node from outside it |
||||
// 目标距离 |
||||
const targetDistance = 200 // 设定一个一般的距离 |
||||
|
||||
// 计算相机新位置 |
||||
const distRatio = 1 + targetDistance / Math.hypot(node.x, node.y, node.z) |
||||
const newPos = { |
||||
x: node.x * distRatio, |
||||
y: node.y * distRatio, |
||||
z: node.z * distRatio, |
||||
} |
||||
|
||||
// 如果节点位于原点,则特殊处理 |
||||
if (node.x === 0 && node.y === 0 && node.z === 0) { |
||||
newPos.z = targetDistance // 将 z 轴设定为目标距离 |
||||
} |
||||
|
||||
// 移动相机至新位置 |
||||
//@ts-ignore |
||||
Graph.cameraPosition( |
||||
newPos, // 新位置 |
||||
node, // 视角焦点 |
||||
3000, // 过渡时长 |
||||
) |
||||
|
||||
// 获取图表数据 |
||||
//@ts-ignore |
||||
const graphData = Graph.graphData() |
||||
|
||||
// 遍历连接线,检查每条连接线的起点和终点是否与当前点击的节点相关联 |
||||
graphData.links.forEach((link: any) => { |
||||
// console.log(link); |
||||
|
||||
if (link.source.id === node.id || link.target.id === node.id) { |
||||
setLabel() |
||||
// 将与当前点击节点相关联的连接线颜色设置为红色 |
||||
// link.color = '#FF0000' // 设置连接线颜色为红色 |
||||
//@ts-ignore |
||||
Graph.linkColor((item: any): any => { |
||||
if (item.source.id === node.id || item.target.id === node.id) { |
||||
return 'red' |
||||
} else { |
||||
return '#dd92fd' |
||||
} |
||||
|
||||
}) |
||||
} else { |
||||
// Graph.linkColor(() => '#a4c7fe') // 设置连接线颜色为红色 |
||||
} |
||||
}) |
||||
|
||||
// 更新图形数据,以应用颜色变化 |
||||
//@ts-ignore |
||||
Graph.graphData(graphData) |
||||
}) |
||||
dom.value = document.querySelector('canvas') as any |
||||
}) |
||||
|
||||
const setLabel = () => { |
||||
//@ts-ignore |
||||
Graph.linkThreeObject((link:any) => { |
||||
// extend link with text sprite |
||||
const sprite = new SpriteText(`${link.label}`); |
||||
sprite.color = '#ccc'; |
||||
sprite.textHeight = 1.5; |
||||
return sprite; |
||||
}) |
||||
//@ts-ignore |
||||
Graph.linkPositionUpdate((sprite, { start, end }) => { |
||||
//@ts-ignore |
||||
const middlePos = Object.assign(...['x', 'y', 'z'].map(c => ({ |
||||
//@ts-ignore |
||||
[c]: start[c] + (end[c] - start[c]) / 2 // calc middle point |
||||
}))); |
||||
|
||||
// Position sprite |
||||
Object.assign(sprite.position, middlePos); |
||||
}); |
||||
//@ts-ignore |
||||
Graph.d3Force('charge').strength(-120); |
||||
} |
||||
// const goToEditAtlas = () => { |
||||
// console.log(jsonData.value) |
||||
|
||||
// $router.push({ name: 'EditAtlas', params: { id: 123 } }) |
||||
// } |
||||
|
||||
</script> |
||||
|
||||
<style lang='scss' scoped> |
||||
|
||||
</style> |
@ -0,0 +1,275 @@ |
||||
<template> |
||||
<div class="fold-info-ui"> |
||||
<div class="demo-collapse"> |
||||
<el-collapse class="collapse" v-model="activeNames" @change="handleChange"> |
||||
<el-collapse-item v-for="item in 3" :key="item" :name="item"> |
||||
<template #title> |
||||
<div class="title"> |
||||
<div class="icon"> |
||||
<img src="@/assets/images/minus-circle-filled.png" alt=""> |
||||
</div> |
||||
<div class="index">01 /</div> |
||||
<div class="text">基本概念</div> |
||||
<div class="title-tag-box"> |
||||
<div class="my-tag" type="warning">32学时</div> |
||||
<div class="my-tag">本章资源</div> |
||||
</div> |
||||
</div> |
||||
</template> |
||||
<template #default> |
||||
<div class="main-box"> |
||||
<div class="left"></div> |
||||
<div class="right"> |
||||
<div class="structure-item"> |
||||
<div class="structure-item-titile-box" v-for="item in 3" :key="item"> |
||||
<div class="titile-box-titile"> |
||||
<div class="sequence">5</div> |
||||
<div class="tit-box"> |
||||
<div class="tit-box-left">1.1 基本概念1</div> |
||||
<div class="tit-box-right my-tag">资源</div> |
||||
<div class="tit-box-edit"> |
||||
<button class="my-button">编辑</button> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
<div class="titile-box-knowledge"> |
||||
<div class="my-tag">知识点1</div> |
||||
<div class="my-tag">知识点1</div> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</template> |
||||
|
||||
</el-collapse-item> |
||||
</el-collapse> |
||||
</div> |
||||
</div> |
||||
</template> |
||||
|
||||
<script setup lang="ts"> |
||||
import { ref } from 'vue' |
||||
const props = defineProps(['foldInfoData']) |
||||
console.log(props.foldInfoData, '可折叠基本概念接口数据'); |
||||
const activeNames = ref([1]) |
||||
const handleChange = (val: string[]) => { |
||||
console.log(val) |
||||
} |
||||
</script> |
||||
|
||||
|
||||
<style lang="scss" scoped> |
||||
::v-deep .collapse { |
||||
border-top: none; |
||||
|
||||
.el-collapse-item__header { |
||||
border-bottom: none; |
||||
} |
||||
} |
||||
|
||||
.fold-info-ui { |
||||
width: 100%; |
||||
height: 100%; |
||||
padding-top: 27px; |
||||
padding-left: 61px; |
||||
background: #fff; |
||||
|
||||
|
||||
.demo-collapse { |
||||
$title-icon-width: 15px; |
||||
$title--icon-m-r: 34px; |
||||
$title-index-width: 60px; |
||||
|
||||
|
||||
.title { |
||||
display: flex; |
||||
height: 22px; |
||||
width: 100%; |
||||
align-items: center; |
||||
|
||||
.icon { |
||||
line-height: 0px; |
||||
width: $title-icon-width; |
||||
height: $title-icon-width; |
||||
margin-right: $title--icon-m-r; |
||||
|
||||
& img { |
||||
width: 100%; |
||||
height: 100%; |
||||
|
||||
} |
||||
} |
||||
|
||||
.index { |
||||
width: $title-index-width; |
||||
text-align: center; |
||||
text-wrap: nowrap; |
||||
font-weight: 400; |
||||
font-size: 32px; |
||||
color: #333333; |
||||
margin-right: 15px; |
||||
} |
||||
|
||||
.text { |
||||
font-weight: bold; |
||||
font-size: 24px; |
||||
color: #333333; |
||||
} |
||||
|
||||
.title-tag-box { |
||||
margin-left: auto; |
||||
margin-right: 76px; |
||||
display: flex; |
||||
column-gap: 16px; |
||||
} |
||||
|
||||
} |
||||
|
||||
.main-box { |
||||
width: 100%; |
||||
display: flex; |
||||
$line: 1px dashed #D9D9D9; |
||||
|
||||
|
||||
.left { |
||||
width: calc($title-icon-width + $title--icon-m-r + $title-index-width / 2); |
||||
border-right: $line; |
||||
} |
||||
|
||||
.right { |
||||
flex-grow: 1; |
||||
|
||||
.structure-item { |
||||
width: 100%; |
||||
|
||||
.structure-item-titile-box { |
||||
&>* { |
||||
padding-left: 42px; |
||||
} |
||||
|
||||
.titile-box-titile { |
||||
&:first-child { |
||||
margin-top: 20px; |
||||
} |
||||
|
||||
position: relative; |
||||
margin-bottom: 15px; |
||||
|
||||
&::before { |
||||
content: ''; |
||||
display: block; |
||||
position: absolute; |
||||
left: 0; |
||||
top: 50%; |
||||
width: 30px; |
||||
border-top: $line; |
||||
} |
||||
|
||||
.sequence { |
||||
$height: 33px; |
||||
width: $height; |
||||
height: $height; |
||||
position: absolute; |
||||
left: 0; |
||||
top: 50%; |
||||
transform: translate(-50%, -50%); |
||||
border-radius: 50%; |
||||
background: #FFB21E; |
||||
text-align: center; |
||||
line-height: $height; |
||||
font-weight: 400; |
||||
font-size: 15px; |
||||
color: #000000; |
||||
} |
||||
|
||||
.tit-box { |
||||
height: 20px; |
||||
display: flex; |
||||
align-items: center; |
||||
gap: 17px; |
||||
|
||||
.tit-box-left { |
||||
font-weight: 400; |
||||
font-size: 20px; |
||||
color: #000000; |
||||
} |
||||
|
||||
// .tit-box-right {} |
||||
.tit-box-edit { |
||||
margin-left: auto; |
||||
margin-right: 72px; |
||||
} |
||||
} |
||||
} |
||||
|
||||
.titile-box-knowledge { |
||||
display: flex; |
||||
margin-left: 33px; |
||||
|
||||
&>* { |
||||
margin-left: 38px; |
||||
border: 1px solid #0052D9; |
||||
font-weight: bold; |
||||
font-size: 12px; |
||||
color: #0052D9; |
||||
|
||||
&:first-child { |
||||
margin-left: 0; |
||||
} |
||||
} |
||||
} |
||||
|
||||
} |
||||
|
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
// =================== |
||||
.my-tag { |
||||
padding: 2px 8px; |
||||
font-weight: 400; |
||||
font-size: 12px; |
||||
color: #0052D9; |
||||
line-height: 20px; |
||||
background: #F2F3FF; |
||||
cursor: pointer; |
||||
border-radius: 3px; |
||||
} |
||||
|
||||
[type=warning].my-tag { |
||||
color: #E37318; |
||||
background: #FFF1E9; |
||||
} |
||||
|
||||
.my-button { |
||||
all: initial; |
||||
$color-start: #52A1FF; |
||||
$color-end: #4255FF; |
||||
padding: 8px 24px; |
||||
background: linear-gradient(128deg, $color-start 0%, $color-end 100%); |
||||
box-shadow: 0px 4px 7px 0px rgba(0, 82, 255, 0.27); |
||||
border-radius: 20px; |
||||
font-weight: bold; |
||||
font-size: 16px; |
||||
color: rgba(255, 255, 255, 0.9); |
||||
line-height: 24px; |
||||
text-align: center; |
||||
cursor: pointer; |
||||
|
||||
&:hover { |
||||
background: linear-gradient(128deg, lighten($color-start, 5%) 0%, lighten($color-end, 5%) 100%); |
||||
} |
||||
|
||||
&:active { |
||||
background: linear-gradient(128deg, darken($color-start, 10%) 0%, darken($color-end, 10%) 100%); |
||||
} |
||||
|
||||
&:disabled { |
||||
background: linear-gradient(128deg, lighten($color-start, 20%) 0%, lighten($color-end, 20%) 100%); |
||||
} |
||||
} |
||||
} |
||||
</style> |
@ -0,0 +1,5 @@ |
||||
export { default as theoryUi } from './theoryUi.vue' |
||||
export { default as objectiveUi } from './objectiveUi.vue' |
||||
export { default as atlasUi } from '../atlasUi.vue' |
||||
export { default as lookResourceUi } from './lookResourceUi.vue' |
||||
export { default as foldInfoUi } from './foldInfoUi.vue' |
@ -0,0 +1,195 @@ |
||||
<template> |
||||
<div class="look-resource-ui" ref="atlas"> |
||||
<div class="head"> |
||||
<div class="log"></div> |
||||
<div class="text">查看资源</div> |
||||
</div> |
||||
<div class="con" v-for="item in 5" :key="item"> |
||||
<div class="chapter-box"> |
||||
<div class="left">第一章</div> |
||||
<div class="right">第二小节</div> |
||||
</div> |
||||
<div class="detailedness-chapter-box"> |
||||
<div class="left"> |
||||
<div class="vertical-dotted-line"></div> |
||||
</div> |
||||
<div class="right"> |
||||
<div class="top">知识点</div> |
||||
<div class="center">知识点详情,知识点内容</div> |
||||
<div class="bottom"> |
||||
<div class="img-box"></div> |
||||
<div class="info-box"> |
||||
<div class="text-name">知识点清单.pdf</div> |
||||
<div class="text-size">15M</div> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</template> |
||||
<script lang="ts" setup> |
||||
const props = defineProps(['lookResource']) |
||||
console.log(props.lookResource, '查看资源接口数据'); |
||||
</script> |
||||
<style lang="scss"> |
||||
.look-resource-ui { |
||||
$padding-left: 18px; |
||||
width: 100%; |
||||
height: 100%; |
||||
background-color: #fff; |
||||
padding: 24px 22px 0 $padding-left; |
||||
display: flex; |
||||
flex-direction: column; |
||||
|
||||
.head { |
||||
$height: 30px; |
||||
display: flex; |
||||
margin-bottom: 32px; |
||||
|
||||
.log { |
||||
width: $height; |
||||
height: $height; |
||||
background: #4782FF; |
||||
border-radius: 4px; |
||||
margin-right: 12px; |
||||
} |
||||
|
||||
.text { |
||||
font-weight: bold; |
||||
font-size: 20px; |
||||
color: #333333; |
||||
line-height: $height; |
||||
} |
||||
} |
||||
|
||||
.con { |
||||
&>* { |
||||
display: flex; |
||||
} |
||||
|
||||
$width: 51px; |
||||
|
||||
.chapter-box { |
||||
$line-height: 22px; |
||||
margin-bottom: 12px; |
||||
|
||||
.left { |
||||
min-width: $width; |
||||
height: 22px; |
||||
margin-right: 16px; |
||||
font-weight: 400; |
||||
font-size: 16px; |
||||
color: #333333; |
||||
line-height: $line-height; |
||||
|
||||
} |
||||
|
||||
.right { |
||||
line-height: $line-height; |
||||
font-weight: 400; |
||||
font-size: 14px; |
||||
color: #9A9A9A; |
||||
} |
||||
|
||||
} |
||||
|
||||
.detailedness-chapter-box { |
||||
|
||||
.left { |
||||
width: $width; |
||||
|
||||
.vertical-dotted-line { |
||||
height: 168px; |
||||
margin-left: calc(53px - $padding-left); |
||||
border-left: 1px dotted #D9D9D9; |
||||
} |
||||
|
||||
} |
||||
|
||||
.right { |
||||
display: flex; |
||||
flex-direction: column; |
||||
|
||||
&>* { |
||||
margin-bottom: 15px; |
||||
} |
||||
|
||||
&>*:last-child { |
||||
margin-bottom: 0; |
||||
} |
||||
|
||||
.top { |
||||
$height: 17px; |
||||
width: 53px; |
||||
height: $height; |
||||
line-height: $height; |
||||
text-align: center; |
||||
background: #5EE183; |
||||
border-radius: 2px; |
||||
font-weight: 400; |
||||
font-size: 12px; |
||||
color: #FFFFFF; |
||||
} |
||||
|
||||
.center { |
||||
$height: 22px; |
||||
height: $height; |
||||
line-height: $height; |
||||
font-weight: 400; |
||||
font-size: 16px; |
||||
color: #333333; |
||||
} |
||||
|
||||
.bottom { |
||||
width: 243px; |
||||
height: 84px; |
||||
background: rgba(255, 255, 255, 0.5); |
||||
border-radius: 4px; |
||||
border: 1px solid #D9D9D9; |
||||
cursor: pointer; |
||||
transition: .3s; |
||||
padding: 17px 0 17px 20px; |
||||
display: flex; |
||||
|
||||
&:hover { |
||||
border: 1px solid #6997fa; |
||||
} |
||||
|
||||
.img-box { |
||||
width: 42px; |
||||
height: 50px; |
||||
background: #4D4D4D; |
||||
margin-right: 23px; |
||||
} |
||||
|
||||
.info-box { |
||||
display: flex; |
||||
flex-direction: column; |
||||
$height: 22px; |
||||
|
||||
&>* { |
||||
height: $height; |
||||
line-height: $height; |
||||
} |
||||
|
||||
.text-name { |
||||
font-weight: 400; |
||||
font-size: 12px; |
||||
color: #333333; |
||||
margin-bottom: 6px; |
||||
} |
||||
|
||||
.text-size { |
||||
font-weight: 400; |
||||
font-size: 12px; |
||||
color: #9A9A9A; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
} |
||||
</style> |
@ -0,0 +1,96 @@ |
||||
<template> |
||||
<div class="objective-ui"> |
||||
<div class="container"> |
||||
<div class="title">课程目标</div> |
||||
<button class="more btn">了解更多</button> |
||||
<div class="content"> |
||||
{{ orogramObjective.con }} |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</template> |
||||
|
||||
<script lang="ts" setup> |
||||
defineProps(['orogramObjective']) |
||||
</script> |
||||
|
||||
<style lang="scss"> |
||||
.objective-ui { |
||||
|
||||
$border-width: 3px; |
||||
$line-height: 17px; |
||||
width: 100%; |
||||
height: 100%; |
||||
|
||||
background: #FFFFFF; |
||||
border: $border-width solid #EDEDED; |
||||
padding: calc(16px - $border-width) 0 $line-height 66px; |
||||
|
||||
button { |
||||
all: initial; |
||||
} |
||||
|
||||
.btn { |
||||
$background-color: #F2F3FF; |
||||
width: 64px; |
||||
height: 18px; |
||||
line-height: 18px; |
||||
text-align: center; |
||||
background-color: $background-color; |
||||
border-radius: 45px; |
||||
font-weight: 400; |
||||
font-size: 12px; |
||||
color: #0052D9; |
||||
cursor: pointer; |
||||
|
||||
&:hover { |
||||
background: darken($background-color, 5%); |
||||
} |
||||
|
||||
&:active { |
||||
background: darken($background-color, 10%); |
||||
} |
||||
|
||||
&:disabled { |
||||
background: lighten($background-color, 20%); |
||||
} |
||||
} |
||||
|
||||
.container { |
||||
$row-height: 23px; |
||||
height: 100%; |
||||
width: 443px; |
||||
display: grid; |
||||
grid-template-columns: repeat(2, auto); |
||||
grid-template-rows: $row-height 1fr; |
||||
row-gap: 25px; |
||||
|
||||
.title { |
||||
font-weight: bold; |
||||
font-size: 20px; |
||||
color: #333333; |
||||
line-height: $row-height; |
||||
text-align: left; |
||||
} |
||||
|
||||
.more { |
||||
justify-self: end; |
||||
} |
||||
|
||||
.content { |
||||
overflow: hidden; |
||||
text-overflow: ellipsis; |
||||
display: -webkit-box; |
||||
-webkit-line-clamp: 5; |
||||
-webkit-box-orient: vertical; |
||||
grid-column: 1/3; |
||||
font-weight: 400; |
||||
font-size: 14px; |
||||
color: #999999; |
||||
line-height: $line-height; |
||||
text-align: left; |
||||
} |
||||
} |
||||
|
||||
} |
||||
</style> |
@ -0,0 +1,116 @@ |
||||
<template> |
||||
<div class="theory-ui"> |
||||
<div class="container"> |
||||
<div class="top"> |
||||
<div class="title">计算机导论</div> |
||||
<button class="my-button">导入</button> |
||||
<button class="my-button">编辑</button> |
||||
</div> |
||||
<div class="bottom"> |
||||
<div class="text-container" v-for="item, index in props.learnInfo" :key="index"> |
||||
<div class="text-name">{{ addUnit(item.name, ':') }}</div> |
||||
<div class="text-value">{{ item.value }}</div> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</template> |
||||
|
||||
<script lang="ts" setup> |
||||
|
||||
function addUnit(viewStr: string, unit: string): string { |
||||
if (viewStr) return `${viewStr}${unit}` |
||||
else return '' |
||||
} |
||||
const props = defineProps(['learnInfo']) |
||||
|
||||
</script> |
||||
|
||||
<style lang="scss"> |
||||
.theory-ui { |
||||
|
||||
width: 100%; |
||||
height: 100%; |
||||
display: flex; |
||||
background-color: #fff; |
||||
|
||||
.container { |
||||
$border-width: 3px; |
||||
flex-grow: 1; |
||||
border: $border-width solid #EDEDED; |
||||
padding-top: calc(10px - $border-width); |
||||
padding-left: 61px; |
||||
display: flex; |
||||
flex-direction: column; |
||||
|
||||
.top { |
||||
display: flex; |
||||
margin-top: 30px; |
||||
margin-bottom: 46px; |
||||
|
||||
.my-button { |
||||
all: initial; |
||||
$color-start: #52A1FF; |
||||
$color-end: #4255FF; |
||||
padding: 8px 24px; |
||||
background: linear-gradient(128deg, $color-start 0%, $color-end 100%); |
||||
box-shadow: 0px 4px 7px 0px rgba(0, 82, 255, 0.27); |
||||
border-radius: 20px; |
||||
font-weight: bold; |
||||
font-size: 16px; |
||||
color: rgba(255, 255, 255, 0.9); |
||||
line-height: 24px; |
||||
text-align: center; |
||||
cursor: pointer; |
||||
|
||||
&:hover { |
||||
background: linear-gradient(128deg, lighten($color-start, 5%) 0%, lighten($color-end, 5%) 100%); |
||||
} |
||||
|
||||
&:active { |
||||
background: linear-gradient(128deg, darken($color-start, 10%) 0%, darken($color-end, 10%) 100%); |
||||
} |
||||
|
||||
&:disabled { |
||||
background: linear-gradient(128deg, lighten($color-start, 20%) 0%, lighten($color-end, 20%) 100%); |
||||
} |
||||
} |
||||
|
||||
&>*:first-child { |
||||
margin-right: 82px; |
||||
} |
||||
|
||||
&>*:last-child { |
||||
margin-left: 20px; |
||||
} |
||||
|
||||
.title { |
||||
width: 208px; |
||||
height: 34px; |
||||
font-size: 36px; |
||||
color: #5577FF; |
||||
} |
||||
} |
||||
|
||||
.bottom { |
||||
display: grid; |
||||
grid-template-columns: repeat(2, 1fr); |
||||
row-gap: 15px; |
||||
|
||||
.text-container { |
||||
font-weight: 400; |
||||
font-size: 15px; |
||||
color: #333333; |
||||
width: 100%; |
||||
height: 20px; |
||||
display: flex; |
||||
|
||||
.text-name { |
||||
width: 93px; |
||||
} |
||||
|
||||
} |
||||
} |
||||
} |
||||
} |
||||
</style> |
@ -0,0 +1,50 @@ |
||||
<template> |
||||
<div class="atlas-ui" ref="atlas"> |
||||
<professionalProfile v-bind="atlasAttribute" /> |
||||
<div class="on-off" @click="()=>$emit('hadRouter')"> |
||||
<el-icon> |
||||
<Right /> |
||||
</el-icon> |
||||
</div> |
||||
</div> |
||||
</template> |
||||
<script lang="ts" setup> |
||||
import { ref, reactive, onMounted } from 'vue' |
||||
import professionalProfile from './KnowledgeGraph.vue' |
||||
const $emit = defineEmits(['hadRouter']) |
||||
const atlas = ref(null) |
||||
const atlasAttribute = reactive({ width: 0, height: 0 }) |
||||
onMounted(() => { |
||||
const el: HTMLElement = atlas.value! |
||||
const width = +getComputedStyle(el).width.slice(0, -2) |
||||
const height = +getComputedStyle(el).height.slice(0, -2) |
||||
atlasAttribute.width = width |
||||
atlasAttribute.height = height |
||||
}) |
||||
|
||||
</script> |
||||
<style lang="scss"> |
||||
.atlas-ui { |
||||
width: 100%; |
||||
height: 100%; |
||||
position: relative; |
||||
overflow: hidden; |
||||
&:hover .on-off { |
||||
transform: translate(0%); |
||||
padding-right: 10px; |
||||
} |
||||
|
||||
.on-off { |
||||
position: absolute; |
||||
top: 0; |
||||
right: 0; |
||||
cursor: pointer; |
||||
padding-top: 5px; |
||||
transform: translate(100%); |
||||
transition: all .3s; |
||||
} |
||||
|
||||
|
||||
|
||||
} |
||||
</style> |
@ -1,14 +1,150 @@ |
||||
<template> |
||||
<div> |
||||
课程章节 |
||||
<div class="course-chapters"> |
||||
<div class="flex-layout"> |
||||
<div class="flex-left"> |
||||
<div class="left" ref="atlasCon"> |
||||
<!-- 知识图谱 --> |
||||
<atlas-ui @hadRouter="handle" /> |
||||
</div> |
||||
<div class="right-top radius-10 vertical-line"> |
||||
<!-- 计算机导论 --> |
||||
<theory-ui :learnInfo="learnInfo" /> |
||||
</div> |
||||
<div class="right-bottom radius-10 vertical-line"> |
||||
<!-- 课程目标 --> |
||||
<objective-ui :orogramObjective="orogramObjective" /> |
||||
</div> |
||||
<div class="bottom"> |
||||
<!-- 可折叠基本概念 --> |
||||
<fold-info-ui :foldInfoData="foldInfoData"/> |
||||
</div> |
||||
</div> |
||||
<div class="flex_right"> |
||||
<div class="top-container"> |
||||
<img src="" alt=""> |
||||
</div> |
||||
<div class="bottom-container radius-10"> |
||||
<!-- 查看资源 --> |
||||
<look-resource-ui :lookResource="lookResource"/> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
|
||||
</template> |
||||
|
||||
<script lang='ts' setup> |
||||
import { } from 'vue' |
||||
import { getLearnInfoApi, getOrogramObjectiveApi } from '@/api/courseChaptersApi' |
||||
import { theoryUi, objectiveUi, atlasUi, lookResourceUi,foldInfoUi } from './components/KnowledgeGraphUi/index' |
||||
import { reactive } from 'vue' |
||||
import { useRouter } from 'vue-router' |
||||
const $router = useRouter() |
||||
|
||||
// 点击知识图谱右上角图标,前往大的知识图谱页面 atlas-ui |
||||
const handle = () => $router.push('/knowledgeAtlas') |
||||
|
||||
// 计算机导论的数据 theory-ui |
||||
const learnInfo: object[] = reactive([]) |
||||
getLearnInfoApi().then(res => learnInfo.push(...res as object[])) |
||||
|
||||
// 课程目标的数据 objective-ui |
||||
const orogramObjective: any = reactive({}) |
||||
getOrogramObjectiveApi().then(res => Object.assign(orogramObjective, res)) |
||||
|
||||
// 查看资源的数据 look-resource-ui |
||||
const lookResource:any = reactive({}) |
||||
|
||||
// 可折叠基本概念的数据 fold-info-ui |
||||
const foldInfoData:any = reactive({}) |
||||
|
||||
</script> |
||||
|
||||
<style lang='scss' scoped> |
||||
.course-chapters { |
||||
width: 100%; |
||||
height: 100%; |
||||
position: relative; |
||||
background: #F2F7FB; |
||||
|
||||
&, |
||||
* { |
||||
box-sizing: border-box; |
||||
} |
||||
|
||||
$right-box-width: 555px; |
||||
|
||||
.radius-10 { |
||||
border-radius: 10px; |
||||
overflow: hidden; |
||||
} |
||||
|
||||
.vertical-line { |
||||
position: relative; |
||||
|
||||
&::before { |
||||
content: ''; |
||||
display: block; |
||||
position: absolute; |
||||
height: 100%; |
||||
width: 12px; |
||||
background-color: #2147FB; |
||||
} |
||||
} |
||||
|
||||
.flex-layout { |
||||
display: flex; |
||||
justify-content: space-between; |
||||
align-items: flex-start; |
||||
|
||||
.flex-left { |
||||
display: grid; |
||||
grid-template-columns: repeat(2, auto); |
||||
width: 1243px; |
||||
|
||||
.left { |
||||
grid-row: 1/3; |
||||
width: 664px; |
||||
height: 424px; |
||||
} |
||||
|
||||
.right-top { |
||||
justify-self: end; |
||||
width: $right-box-width; |
||||
height: 243px; |
||||
} |
||||
|
||||
.right-bottom { |
||||
justify-self: end; |
||||
align-self: end; |
||||
width: $right-box-width; |
||||
height: 168px; |
||||
} |
||||
|
||||
.bottom { |
||||
margin-top: 15px; |
||||
grid-column: 1/3; |
||||
width: 100%; |
||||
min-height: 521px; |
||||
} |
||||
} |
||||
|
||||
.flex_right { |
||||
width: 355px; |
||||
|
||||
.top-container { |
||||
height: 231px; |
||||
margin-bottom: 23px; |
||||
img{ |
||||
width: 100%; |
||||
height: 100%; |
||||
} |
||||
} |
||||
|
||||
.bottom-container { |
||||
min-height: 945px; |
||||
} |
||||
} |
||||
} |
||||
|
||||
} |
||||
</style> |
||||
|
@ -0,0 +1,14 @@ |
||||
<template> |
||||
<div class="knowledge-atlas"> |
||||
<atlas-ui @hadRouter="handle" /> |
||||
</div> |
||||
</template> |
||||
|
||||
<script lang='ts' setup> |
||||
import { atlasUi } from './components/KnowledgeGraphUi/index' |
||||
import { useRouter } from 'vue-router'; |
||||
const $router = useRouter() |
||||
const handle = () => $router.push('/curriculumCenter/courseChapters') |
||||
</script> |
||||
|
||||
<style lang='scss' scoped></style> |
@ -0,0 +1,468 @@ |
||||
import { LinearFilter, Sprite, SpriteMaterial, SRGBColorSpace, Texture } from 'three'; |
||||
|
||||
function _callSuper(t, o, e) { |
||||
return o = _getPrototypeOf(o), _possibleConstructorReturn(t, _isNativeReflectConstruct() ? Reflect.construct(o, e || [], _getPrototypeOf(t).constructor) : o.apply(t, e)); |
||||
} |
||||
function _isNativeReflectConstruct() { |
||||
try { |
||||
var t = !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); |
||||
} catch (t) {} |
||||
return (_isNativeReflectConstruct = function () { |
||||
return !!t; |
||||
})(); |
||||
} |
||||
function _iterableToArrayLimit(r, l) { |
||||
var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; |
||||
if (null != t) { |
||||
var e, |
||||
n, |
||||
i, |
||||
u, |
||||
a = [], |
||||
f = !0, |
||||
o = !1; |
||||
try { |
||||
if (i = (t = t.call(r)).next, 0 === l) { |
||||
if (Object(t) !== t) return; |
||||
f = !1; |
||||
} else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); |
||||
} catch (r) { |
||||
o = !0, n = r; |
||||
} finally { |
||||
try { |
||||
if (!f && null != t.return && (u = t.return(), Object(u) !== u)) return; |
||||
} finally { |
||||
if (o) throw n; |
||||
} |
||||
} |
||||
return a; |
||||
} |
||||
} |
||||
function _toPrimitive(t, r) { |
||||
if ("object" != typeof t || !t) return t; |
||||
var e = t[Symbol.toPrimitive]; |
||||
if (void 0 !== e) { |
||||
var i = e.call(t, r || "default"); |
||||
if ("object" != typeof i) return i; |
||||
throw new TypeError("@@toPrimitive must return a primitive value."); |
||||
} |
||||
return ("string" === r ? String : Number)(t); |
||||
} |
||||
function _toPropertyKey(t) { |
||||
var i = _toPrimitive(t, "string"); |
||||
return "symbol" == typeof i ? i : String(i); |
||||
} |
||||
function _classCallCheck(instance, Constructor) { |
||||
if (!(instance instanceof Constructor)) { |
||||
throw new TypeError("Cannot call a class as a function"); |
||||
} |
||||
} |
||||
function _defineProperties(target, props) { |
||||
for (var i = 0; i < props.length; i++) { |
||||
var descriptor = props[i]; |
||||
descriptor.enumerable = descriptor.enumerable || false; |
||||
descriptor.configurable = true; |
||||
if ("value" in descriptor) descriptor.writable = true; |
||||
Object.defineProperty(target, _toPropertyKey(descriptor.key), descriptor); |
||||
} |
||||
} |
||||
function _createClass(Constructor, protoProps, staticProps) { |
||||
if (protoProps) _defineProperties(Constructor.prototype, protoProps); |
||||
if (staticProps) _defineProperties(Constructor, staticProps); |
||||
Object.defineProperty(Constructor, "prototype", { |
||||
writable: false |
||||
}); |
||||
return Constructor; |
||||
} |
||||
function _inherits(subClass, superClass) { |
||||
if (typeof superClass !== "function" && superClass !== null) { |
||||
throw new TypeError("Super expression must either be null or a function"); |
||||
} |
||||
subClass.prototype = Object.create(superClass && superClass.prototype, { |
||||
constructor: { |
||||
value: subClass, |
||||
writable: true, |
||||
configurable: true |
||||
} |
||||
}); |
||||
Object.defineProperty(subClass, "prototype", { |
||||
writable: false |
||||
}); |
||||
if (superClass) _setPrototypeOf(subClass, superClass); |
||||
} |
||||
function _getPrototypeOf(o) { |
||||
_getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf.bind() : function _getPrototypeOf(o) { |
||||
return o.__proto__ || Object.getPrototypeOf(o); |
||||
}; |
||||
return _getPrototypeOf(o); |
||||
} |
||||
function _setPrototypeOf(o, p) { |
||||
_setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function _setPrototypeOf(o, p) { |
||||
o.__proto__ = p; |
||||
return o; |
||||
}; |
||||
return _setPrototypeOf(o, p); |
||||
} |
||||
function _assertThisInitialized(self) { |
||||
if (self === void 0) { |
||||
throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); |
||||
} |
||||
return self; |
||||
} |
||||
function _possibleConstructorReturn(self, call) { |
||||
if (call && (typeof call === "object" || typeof call === "function")) { |
||||
return call; |
||||
} else if (call !== void 0) { |
||||
throw new TypeError("Derived constructors may only return object or undefined"); |
||||
} |
||||
return _assertThisInitialized(self); |
||||
} |
||||
function _slicedToArray(arr, i) { |
||||
return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); |
||||
} |
||||
function _toConsumableArray(arr) { |
||||
return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); |
||||
} |
||||
function _arrayWithoutHoles(arr) { |
||||
if (Array.isArray(arr)) return _arrayLikeToArray(arr); |
||||
} |
||||
function _arrayWithHoles(arr) { |
||||
if (Array.isArray(arr)) return arr; |
||||
} |
||||
function _iterableToArray(iter) { |
||||
if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter); |
||||
} |
||||
function _unsupportedIterableToArray(o, minLen) { |
||||
if (!o) return; |
||||
if (typeof o === "string") return _arrayLikeToArray(o, minLen); |
||||
var n = Object.prototype.toString.call(o).slice(8, -1); |
||||
if (n === "Object" && o.constructor) n = o.constructor.name; |
||||
if (n === "Map" || n === "Set") return Array.from(o); |
||||
if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); |
||||
} |
||||
function _arrayLikeToArray(arr, len) { |
||||
if (len == null || len > arr.length) len = arr.length; |
||||
for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; |
||||
return arr2; |
||||
} |
||||
function _nonIterableSpread() { |
||||
throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); |
||||
} |
||||
function _nonIterableRest() { |
||||
throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); |
||||
} |
||||
|
||||
var three = typeof window !== 'undefined' && window.THREE ? window.THREE // Prefer consumption from global THREE, if exists
|
||||
: { |
||||
LinearFilter: LinearFilter, |
||||
Sprite: Sprite, |
||||
SpriteMaterial: SpriteMaterial, |
||||
SRGBColorSpace: SRGBColorSpace, |
||||
Texture: Texture |
||||
}; |
||||
var _default = /*#__PURE__*/function (_three$Sprite) { |
||||
_inherits(_default, _three$Sprite); |
||||
function _default() { |
||||
var _this; |
||||
var text = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : ''; |
||||
var textHeight = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 10; |
||||
var color = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 'rgba(255, 255, 255, 1)'; |
||||
_classCallCheck(this, _default); |
||||
_this = _callSuper(this, _default, [new three.SpriteMaterial()]); |
||||
_this._text = "".concat(text); |
||||
_this._textHeight = textHeight; |
||||
_this._color = color; |
||||
_this._backgroundColor = false; // no background color
|
||||
|
||||
_this._padding = 0; |
||||
_this._borderWidth = 0; |
||||
_this._borderRadius = 0; |
||||
_this._borderColor = 'white'; |
||||
_this._strokeWidth = 0; |
||||
_this._strokeColor = 'white'; |
||||
_this._fontFace = 'system-ui'; |
||||
_this._fontSize = 90; // defines text resolution
|
||||
_this._fontWeight = 'normal'; |
||||
_this._canvas = document.createElement('canvas'); |
||||
_this._genCanvas(); |
||||
return _this; |
||||
} |
||||
_createClass(_default, [{ |
||||
key: "text", |
||||
get: function get() { |
||||
return this._text; |
||||
}, |
||||
set: function set(text) { |
||||
this._text = text; |
||||
this._genCanvas(); |
||||
} |
||||
}, { |
||||
key: "textHeight", |
||||
get: function get() { |
||||
return this._textHeight; |
||||
}, |
||||
set: function set(textHeight) { |
||||
this._textHeight = textHeight; |
||||
this._genCanvas(); |
||||
} |
||||
}, { |
||||
key: "color", |
||||
get: function get() { |
||||
return this._color; |
||||
}, |
||||
set: function set(color) { |
||||
this._color = color; |
||||
this._genCanvas(); |
||||
} |
||||
}, { |
||||
key: "backgroundColor", |
||||
get: function get() { |
||||
return this._backgroundColor; |
||||
}, |
||||
set: function set(color) { |
||||
this._backgroundColor = color; |
||||
this._genCanvas(); |
||||
} |
||||
}, { |
||||
key: "padding", |
||||
get: function get() { |
||||
return this._padding; |
||||
}, |
||||
set: function set(padding) { |
||||
this._padding = padding; |
||||
this._genCanvas(); |
||||
} |
||||
}, { |
||||
key: "borderWidth", |
||||
get: function get() { |
||||
return this._borderWidth; |
||||
}, |
||||
set: function set(borderWidth) { |
||||
this._borderWidth = borderWidth; |
||||
this._genCanvas(); |
||||
} |
||||
}, { |
||||
key: "borderRadius", |
||||
get: function get() { |
||||
return this._borderRadius; |
||||
}, |
||||
set: function set(borderRadius) { |
||||
this._borderRadius = borderRadius; |
||||
this._genCanvas(); |
||||
} |
||||
}, { |
||||
key: "borderColor", |
||||
get: function get() { |
||||
return this._borderColor; |
||||
}, |
||||
set: function set(borderColor) { |
||||
this._borderColor = borderColor; |
||||
this._genCanvas(); |
||||
} |
||||
}, { |
||||
key: "fontFace", |
||||
get: function get() { |
||||
return this._fontFace; |
||||
}, |
||||
set: function set(fontFace) { |
||||
this._fontFace = fontFace; |
||||
this._genCanvas(); |
||||
} |
||||
}, { |
||||
key: "fontSize", |
||||
get: function get() { |
||||
return this._fontSize; |
||||
}, |
||||
set: function set(fontSize) { |
||||
this._fontSize = fontSize; |
||||
this._genCanvas(); |
||||
} |
||||
}, { |
||||
key: "fontWeight", |
||||
get: function get() { |
||||
return this._fontWeight; |
||||
}, |
||||
set: function set(fontWeight) { |
||||
this._fontWeight = fontWeight; |
||||
this._genCanvas(); |
||||
} |
||||
}, { |
||||
key: "strokeWidth", |
||||
get: function get() { |
||||
return this._strokeWidth; |
||||
}, |
||||
set: function set(strokeWidth) { |
||||
this._strokeWidth = strokeWidth; |
||||
this._genCanvas(); |
||||
} |
||||
}, { |
||||
key: "strokeColor", |
||||
get: function get() { |
||||
return this._strokeColor; |
||||
}, |
||||
set: function set(strokeColor) { |
||||
this._strokeColor = strokeColor; |
||||
this._genCanvas(); |
||||
} |
||||
}, { |
||||
key: "_genCanvas", |
||||
value: function _genCanvas() { |
||||
var _this2 = this; |
||||
var canvas = this._canvas; |
||||
var ctx = canvas.getContext('2d'); |
||||
var border = Array.isArray(this.borderWidth) ? this.borderWidth : [this.borderWidth, this.borderWidth]; // x,y border
|
||||
var relBorder = border.map(function (b) { |
||||
return b * _this2.fontSize * 0.1; |
||||
}); // border in canvas units
|
||||
|
||||
var borderRadius = Array.isArray(this.borderRadius) ? this.borderRadius : [this.borderRadius, this.borderRadius, this.borderRadius, this.borderRadius]; // tl tr br bl corners
|
||||
var relBorderRadius = borderRadius.map(function (b) { |
||||
return b * _this2.fontSize * 0.1; |
||||
}); // border radius in canvas units
|
||||
|
||||
var padding = Array.isArray(this.padding) ? this.padding : [this.padding, this.padding]; // x,y padding
|
||||
var relPadding = padding.map(function (p) { |
||||
return p * _this2.fontSize * 0.1; |
||||
}); // padding in canvas units
|
||||
|
||||
var lines = this.text.split('\n'); |
||||
var font = "".concat(this.fontWeight, " ").concat(this.fontSize, "px ").concat(this.fontFace); |
||||
ctx.font = font; // measure canvas with appropriate font
|
||||
var innerWidth = Math.max.apply(Math, _toConsumableArray(lines.map(function (line) { |
||||
return ctx.measureText(line).width; |
||||
}))); |
||||
var innerHeight = this.fontSize * lines.length; |
||||
canvas.width = innerWidth + relBorder[0] * 2 + relPadding[0] * 2; |
||||
canvas.height = innerHeight + relBorder[1] * 2 + relPadding[1] * 2; |
||||
|
||||
// paint border
|
||||
if (this.borderWidth) { |
||||
ctx.strokeStyle = this.borderColor; |
||||
if (relBorder[0]) { |
||||
// left + right borders
|
||||
var hb = relBorder[0] / 2; |
||||
ctx.lineWidth = relBorder[0]; |
||||
ctx.beginPath(); |
||||
ctx.moveTo(hb, relBorderRadius[0]); |
||||
ctx.lineTo(hb, canvas.height - relBorderRadius[3]); |
||||
ctx.moveTo(canvas.width - hb, relBorderRadius[1]); |
||||
ctx.lineTo(canvas.width - hb, canvas.height - relBorderRadius[2]); |
||||
ctx.stroke(); |
||||
} |
||||
if (relBorder[1]) { |
||||
// top + bottom borders
|
||||
var _hb = relBorder[1] / 2; |
||||
ctx.lineWidth = relBorder[1]; |
||||
ctx.beginPath(); |
||||
ctx.moveTo(Math.max(relBorder[0], relBorderRadius[0]), _hb); |
||||
ctx.lineTo(canvas.width - Math.max(relBorder[0], relBorderRadius[1]), _hb); |
||||
ctx.moveTo(Math.max(relBorder[0], relBorderRadius[3]), canvas.height - _hb); |
||||
ctx.lineTo(canvas.width - Math.max(relBorder[0], relBorderRadius[2]), canvas.height - _hb); |
||||
ctx.stroke(); |
||||
} |
||||
if (this.borderRadius) { |
||||
// strike rounded corners
|
||||
var cornerWidth = Math.max.apply(Math, _toConsumableArray(relBorder)); |
||||
var _hb2 = cornerWidth / 2; |
||||
ctx.lineWidth = cornerWidth; |
||||
ctx.beginPath(); |
||||
[!!relBorderRadius[0] && [relBorderRadius[0], _hb2, _hb2, relBorderRadius[0]], !!relBorderRadius[1] && [canvas.width - relBorderRadius[1], canvas.width - _hb2, _hb2, relBorderRadius[1]], !!relBorderRadius[2] && [canvas.width - relBorderRadius[2], canvas.width - _hb2, canvas.height - _hb2, canvas.height - relBorderRadius[2]], !!relBorderRadius[3] && [relBorderRadius[3], _hb2, canvas.height - _hb2, canvas.height - relBorderRadius[3]]].filter(function (d) { |
||||
return d; |
||||
}).forEach(function (_ref) { |
||||
var _ref2 = _slicedToArray(_ref, 4), |
||||
x0 = _ref2[0], |
||||
x1 = _ref2[1], |
||||
y0 = _ref2[2], |
||||
y1 = _ref2[3]; |
||||
ctx.moveTo(x0, y0); |
||||
ctx.quadraticCurveTo(x1, y0, x1, y1); |
||||
}); |
||||
ctx.stroke(); |
||||
} |
||||
} |
||||
|
||||
// paint background
|
||||
if (this.backgroundColor) { |
||||
ctx.fillStyle = this.backgroundColor; |
||||
if (!this.borderRadius) { |
||||
ctx.fillRect(relBorder[0], relBorder[1], canvas.width - relBorder[0] * 2, canvas.height - relBorder[1] * 2); |
||||
} else { |
||||
// fill with rounded corners
|
||||
ctx.beginPath(); |
||||
ctx.moveTo(relBorder[0], relBorderRadius[0]); |
||||
[[relBorder[0], relBorderRadius[0], canvas.width - relBorderRadius[1], relBorder[1], relBorder[1], relBorder[1]], |
||||
// t
|
||||
[canvas.width - relBorder[0], canvas.width - relBorder[0], canvas.width - relBorder[0], relBorder[1], relBorderRadius[1], canvas.height - relBorderRadius[2]], |
||||
// r
|
||||
[canvas.width - relBorder[0], canvas.width - relBorderRadius[2], relBorderRadius[3], canvas.height - relBorder[1], canvas.height - relBorder[1], canvas.height - relBorder[1]], |
||||
// b
|
||||
[relBorder[0], relBorder[0], relBorder[0], canvas.height - relBorder[1], canvas.height - relBorderRadius[3], relBorderRadius[0]] // t
|
||||
].forEach(function (_ref3) { |
||||
var _ref4 = _slicedToArray(_ref3, 6), |
||||
x0 = _ref4[0], |
||||
x1 = _ref4[1], |
||||
x2 = _ref4[2], |
||||
y0 = _ref4[3], |
||||
y1 = _ref4[4], |
||||
y2 = _ref4[5]; |
||||
ctx.quadraticCurveTo(x0, y0, x1, y1); |
||||
ctx.lineTo(x2, y2); |
||||
}); |
||||
ctx.closePath(); |
||||
ctx.fill(); |
||||
} |
||||
} |
||||
ctx.translate.apply(ctx, _toConsumableArray(relBorder)); |
||||
ctx.translate.apply(ctx, _toConsumableArray(relPadding)); |
||||
|
||||
// paint text
|
||||
ctx.font = font; // Set font again after canvas is resized, as context properties are reset
|
||||
ctx.fillStyle = this.color; |
||||
ctx.textBaseline = 'bottom'; |
||||
var drawTextStroke = this.strokeWidth > 0; |
||||
if (drawTextStroke) { |
||||
ctx.lineWidth = this.strokeWidth * this.fontSize / 10; |
||||
ctx.strokeStyle = this.strokeColor; |
||||
} |
||||
lines.forEach(function (line, index) { |
||||
var lineX = (innerWidth - ctx.measureText(line).width) / 2; |
||||
var lineY = (index + 1) * _this2.fontSize; |
||||
drawTextStroke && ctx.strokeText(line, lineX, lineY); |
||||
ctx.fillText(line, lineX, lineY); |
||||
}); |
||||
|
||||
// Inject canvas into sprite
|
||||
if (this.material.map) this.material.map.dispose(); // gc previous texture
|
||||
var texture = this.material.map = new three.Texture(canvas); |
||||
texture.minFilter = three.LinearFilter; |
||||
texture.colorSpace = three.SRGBColorSpace; |
||||
texture.needsUpdate = true; |
||||
var yScale = this.textHeight * lines.length + border[1] * 2 + padding[1] * 2; |
||||
this.scale.set(yScale * canvas.width / canvas.height, yScale, 0); |
||||
} |
||||
}, { |
||||
key: "clone", |
||||
value: function clone() { |
||||
return new this.constructor(this.text, this.textHeight, this.color).copy(this); |
||||
} |
||||
}, { |
||||
key: "copy", |
||||
value: function copy(source) { |
||||
three.Sprite.prototype.copy.call(this, source); |
||||
this.color = source.color; |
||||
this.backgroundColor = source.backgroundColor; |
||||
this.padding = source.padding; |
||||
this.borderWidth = source.borderWidth; |
||||
this.borderColor = source.borderColor; |
||||
this.fontFace = source.fontFace; |
||||
this.fontSize = source.fontSize; |
||||
this.fontWeight = source.fontWeight; |
||||
this.strokeWidth = source.strokeWidth; |
||||
this.strokeColor = source.strokeColor; |
||||
return this; |
||||
} |
||||
}]); |
||||
return _default; |
||||
}(three.Sprite); |
||||
|
||||
export { _default as default }; |
Loading…
Reference in new issue