1
0
mirror of https://gitee.com/coder-xiaomo/algorithm-visualization synced 2025-09-10 06:31:37 +08:00
Code Issues Projects Releases Wiki Activity GitHub Gitee

import webpack

This commit is contained in:
2022-05-18 15:27:44 +08:00
parent cbd867e5f4
commit 065ef077c0
42 changed files with 7526 additions and 2 deletions

20
src/assets/css/index.css Normal file
View File

@@ -0,0 +1,20 @@
html,
body,
.main {
height: 100%;
margin: 0;
padding: 0;
}
.main {
display: grid;
place-items: center;
}
#container {
border: 1px solid #999;
border-radius: 5px;
box-shadow: 0 0 5px #999;
/* width: 700px; */
/* height: 350px; */
}

View File

@@ -0,0 +1,244 @@
/**
* 排序算法父类 Sort v0.1.0
*
* @author coder-xiaomo
* @date 2022-05-16
*/
class Sort {
static animation = null
constructor(animation) {
this.animation = animation
console.log("animation", animation)
}
info() {
return {
name: "未命名算法"
}
}
sort(array) {
console.log("this method was not implement.")
return array
}
doSortWithAnimation(elementId) {
let customAttr = document.getElementById(elementId).customAttr
let array = JSON.parse(JSON.stringify(customAttr.nodes)) // 深拷贝一个数组
console.log(customAttr, array)
this.sortWithAnimation(customAttr, array)
}
sortWithAnimation(array) {
console.log("this method was not implement.")
return array
}
/**
* 交换数组中的两个元素
* @param {*} array 数组
* @param {*} index1 元素1索引
* @param {*} index2 元素2索引
*/
swap(array, index1, index2) {
if (index1 < 0 || index1 >= array.length)
throw new Error("index1索引超限")
if (index2 < 0 || index2 >= array.length)
throw new Error("index2索引超限")
if (index1 == index2)
return
let tmp = array[index1]
array[index1] = array[index2]
array[index2] = tmp
// only for test
// console.log(Object.keys(array))
// console.log(array, index1, index2)
}
swapAnimation(id, index1, index2) {
if (index1 == index2)
return
animation.swapLinkedListItems(id, [index1, index2])
}
}
/**
* 快速排序算法 Qucik Sort v0.1.0
*
* @author coder-xiaomo
* @date 2022-05-16
*/
class QuickSort extends Sort {
/**
* step
*
* 1. 首先设定一个分界值,通过该分界值将数组分成左右两部分
* 2. 将大于或等于分界值的数据集中到数组右边,小于的集中到左边
* 3. 左右两侧的数组分别重复步骤 2.
* 4. 完成排序
*/
info() {
return {
name: "快速排序算法"
}
}
/*
每个(未排序)的部分
将第一个元素设为 pivot
存储索引 = pivot索引 +1
从 i=pivot指数 +1 到 最右索引 的遍历
如果 a[i] < a[pivot]
交换 (i, 存储索引); 存储索引++;
交换(pivot, 存储索引 - 1)
*/
sort(array, left, right) {
if (typeof (left) === "undefined") left = 0
if (typeof (right) === "undefined") right = array.length - 1
if (right <= left)
return array // 递归出口
const pivot = left // 第一个元素的索引
let p = left + 1
for (let i = left + 1; i <= right; i++) {
if (array[i] < array[pivot]) {
this.swap(array, i, p)
p++
}
}
this.swap(array, pivot, p - 1)
// 左、右分别排序(索引超限在 sort 内部进行处理,此处无需判断)
this.sort(array, pivot, p - 2)
this.sort(array, p, right)
return array
}
sortWithAnimation(customAttr, array, left, right) {
if (typeof (left) === "undefined") left = 0
if (typeof (right) === "undefined") right = array.length - 1
if (right <= left)
return array // 递归出口
const pivot = left // 第一个元素的索引
let p = left + 1
for (let i = left + 1; i <= right; i++) {
animation.highlightLinkedListItems(customAttr.id, [i, pivot])
if (array[i] < array[pivot]) {
this.swap(array, i, p)
this.swapAnimation(customAttr.id, i, p)
p++
}
}
this.swap(array, pivot, p - 1)
this.swapAnimation(customAttr.id, pivot, p - 1)
// 左、右分别排序(索引超限在 sort 内部进行处理,此处无需判断)
this.sortWithAnimation(customAttr, array, pivot, p - 2)
this.sortWithAnimation(customAttr, array, p, right)
return array
}
}
/**
* 冒泡排序算法 Bubble Sort v0.1.0
*
* @author coder-xiaomo
* @date 2022-05-16
*/
class BubbleSort extends Sort {
info() {
return {
name: "冒泡排序算法"
}
}
/*
swapped = false
从 i = 1 到 最后一个没有排序过元素的索引-1
如果 左边元素 > 右边元素
交换(左边元素,右边元素)
swapped = true; ++swapCounter交换计数器
while swapped
*/
sort(array) {
let sortTime = 0
let swapped
do {
swapped = false
for (let i = 1; i < array.length - sortTime; i++) {
if (array[i - 1] > array[i]) {
this.swap(array, i - 1, i)
swapped = true
}
}
sortTime++
} while (swapped)
return array
}
sortWithAnimation(customAttr, array) {
let sortTime = 0
let swapped
do {
swapped = false
for (let i = 1; i < array.length - sortTime; i++) {
animation.highlightLinkedListItems(customAttr.id, [i - 1, i])
if (array[i - 1] > array[i]) {
this.swap(array, i - 1, i)
this.swapAnimation(customAttr.id, i - 1, i)
swapped = true
}
}
sortTime++
} while (swapped)
return array
}
}
/**
* 选择排序算法 Selection Sort v0.1.0
*
* @author coder-xiaomo
* @date 2022-05-16
*/
class SelectionSort extends Sort {
info() {
return {
name: "选择排序算法"
}
}
/*
重复(元素个数-1
把第一个没有排序过的元素设置为最小值
遍历每个没有排序过的元素
如果元素 < 现在的最小值
将此元素设置成为新的最小值
将最小值和第一个没有排序过的位置交换
*/
sort(array) {
let minIndex
for (let i = 0; i < array.length; i++) {
minIndex = i
for (let j = i + 1; j < array.length; j++) {
if (array[minIndex] > array[j]) {
this.swap(array, minIndex, j)
}
}
}
return array
}
}

View File

@@ -0,0 +1,71 @@
/**
* 排序算法测试
*/
/**
* 创建一个随机数数组
* @returns
*/
function getRandomData(count = 20) {
var randomData = []
for (let i = 0; i < count; i++) // 生成随机数
randomData.push(Math.ceil(Math.random() * 100 - 50))
return randomData
}
var data = [
[],
[-1],
[1, 2],
[1, 2, 3, 4, 5, 6, 7, 8],
[8, 7, 6, 5, 4, 3, 2, 1],
[1, 1, 1, 1, 1, 1, 1],
[5, 8, 7, 4, 3, 1, 6, 2, 6, 5],
[47, 11, 50, 13, 16, 49, 8, 9, 38, 27, 20],
// getRandomData(6),
]
var check = [
[],
[-1],
[1, 2],
[1, 2, 3, 4, 5, 6, 7, 8],
[1, 2, 3, 4, 5, 6, 7, 8],
[1, 1, 1, 1, 1, 1, 1],
[1, 2, 3, 4, 5, 5, 6, 6, 7, 8],
[8, 9, 11, 13, 16, 20, 27, 38, 47, 49, 50],
// null
]
var sortAlgorithm = {
quicksort: new QuickSort(animation),
bubblesort: new BubbleSort(animation),
selectionSort: new SelectionSort(animation),
}
// 遍历每一种算法
Object.values(sortAlgorithm).forEach(sortAlgo => {
// 输出调试信息
console.log("算法信息", sortAlgo.info())
data.forEach(element => {
// 将数组元素进行深拷贝
var input = JSON.parse(JSON.stringify(element))
// 进行排序
var result = sortAlgo.sort(input)
console.log(input, result)
// 与结果进行对比,判断是否正确
if (data.indexOf(element) > -1) {
var rightSortResult = check[data.indexOf(element)];
if (rightSortResult) {
if (JSON.stringify(rightSortResult) !== JSON.stringify(result)) {
console.warn("👆结果不匹配!正确结果为", rightSortResult)
} else {
console.info("👆正确")
}
} else {
console.log("👆缺少正确答案,跳过")
}
}
});
// 输出一个空行,便于观察
console.log("-----------------------------------------------")
});

364
src/assets/js/class.js Normal file
View File

@@ -0,0 +1,364 @@
/**
* WorkSpace class v0.1.0
*
* @author coder-xiaomo
* @date 2022-05-15
*
* Released under the MIT license
*/
class WorkSpace {
static settings = null;
static primaryCanvas = null;
constructor(settings) {
this.settings = settings
// 清除原有内容
settings.container
.style("width", settings.width)
.style("height", settings.height)
// .attr("width", settings.width)
// .attr("height", settings.height)
.html("")
// 创建工作区SVG
this.primaryCanvas = settings.container.append("svg")
// 添加id
.attr("id", "primaryCanvas")
// 设置 SVG 宽高
.attr("width", "100%")
.attr("height", "100%")
// 背景色
.style("background-color", settings.colorMap["background"])
}
}
/**
* Shape class v0.1.0
*
* @author coder-xiaomo
* @date 2022-05-15
*
* Released under the MIT license
*/
class Shape {
static workSpace = null;
constructor(workSpace) {
this.workSpace = workSpace
}
addShape(shape, id) {
var settings = this.workSpace.settings
return workSpace.primaryCanvas.append(shape)
.style("transform", `translate(${settings.margin.left}px, ${settings.margin.top}px)`)
.attr("id", id)
}
addShape_NoTransform(shape, id) {
return workSpace.primaryCanvas.append(shape)
.attr("id", id)
}
// 添加矩形
rectangle(id, { x, y, width, height, fillColor = "white", strokeColor = "black" }) {
return this.addShape("rect", id)
.attr("x", x)
.attr("y", y)
.attr("width", width)
.attr("height", height)
.style("fill", fillColor)
.style("stroke", strokeColor)
}
// 添加圆形
circle(id, { x, y, radius, fillColor = "white", strokeColor = "black" }) {
return this.addShape("circle", id)
.attr("cx", x)
.attr("cy", y)
.attr("r", radius)
.style("fill", fillColor)
.style("stroke", strokeColor)
}
// 添加文本
text(id, { x, y, text, fillColor }) {
return this.addShape("text", id)
.attr("x", x)
.attr("y", y)
.text(text)
.style("fill", fillColor)
}
// 添加线
line(id, { x1, y1, x2, y2, strokeColor }) {
return this.addShape("line", id)
.attr("x1", x1)
.attr("y1", y1)
.attr("x2", x2)
.attr("y2", y2)
.style("stroke", strokeColor)
}
// 添加路径
path(id, d, { fillColor = "white", strokeColor = "black" }) {
return this.addShape("path", id)
.attr("d", d)
.style("fill", fillColor)
.style("stroke", strokeColor)
}
// 添加坐标轴
axis(id, { transform, axis }) {
return this.addShape_NoTransform("g", id)
.attr("transform", transform)
.call(axis)
}
// 添加一个链表节点
addNode(id, x, y, width, height, text) {
var primaryCanvas = workSpace.primaryCanvas
primaryCanvas.append("rect", id)
.attr("x", x)
.attr("y", y)
.attr("width", width)
.attr("height", height)
.style("fill", workSpace.settings.colorMap["fill"])
.style("stroke", workSpace.settings.colorMap["stroke"])
primaryCanvas.append("text", id + "_text")
.attr("x", x)
.attr("y", y)
// .attr("x", x + width / 2)
// .attr("y", y + height / 2)
.text(text)
.style("fill", workSpace.settings.colorMap["text"])
}
// 绘制一个链表
getLinkedListFragment(id, nodes) {
var settings = this.workSpace.settings
let displayMaxWidth = 1 * settings.innerSize.width
let displayMaxHeight = 1 * settings.innerSize.height
let areaWidth = 1 * nodes.length // 按照1个Unit来计算
let areaHeight = 1 // 按照1个Unit来计算
let oneUnit = 100
// 可以假设高度相等比较宽度,这样好理解
if (displayMaxWidth / displayMaxHeight > areaWidth / areaHeight) {
// 展示区域左右有多余空间
oneUnit = displayMaxHeight / areaHeight
} else {
// 展示区域上下有空间(或刚刚好)
oneUnit = displayMaxWidth / areaWidth
}
// 定义最大值
if (oneUnit > 120) oneUnit = 120
let fragment = document.createDocumentFragment()
fragment.customAttr = {
id: id,
nodes: nodes,
type: "linkedList",
oneUnit: oneUnit,
gsapTimeline: gsap.timeline({
onComplete: function () {
console.log("all done")
// this.seek(0)
}
})
}
console.log(fragment.customAttr)
// <g></g> 元素不能设置 width 和 height
let g = d3.select(fragment)
.append("svg:g")
.attr("id", id)
.attr("fill", "white")
.attr("transform", `translate(${settings.margin.left}, ${settings.margin.top})`)
for (let i = 0; i < nodes.length; i++) {
let node = nodes[i]
let _g = g.append("svg:g")
_g.append("svg:rect")
.attr("x", i * oneUnit)
.attr("y", 0)
.attr("width", oneUnit)
.attr("height", oneUnit)
.attr("fill", settings.colorMap["fill"])
.attr("stroke", settings.colorMap["stroke"])
_g.append("svg:text")
.text(node)
.attr("x", i * oneUnit + oneUnit / 2)
.attr("y", oneUnit / 2)
.attr("width", oneUnit)
.attr("height", oneUnit)
.attr("fill", settings.colorMap["text"])
// console.log(text.node().getBBox())
// console.log(text.node().getBoundingClientRect())
}
// g.append("svg:rect")
// .attr("width", oneUnit * nodes.length)
// .attr("height", oneUnit)
// .style("fill", "none")
// .style("stroke", "green")
return fragment
}
}
class VectorAnimation {
constructor(workSpace) {
this.workSpace = workSpace
}
swapElementAttr(attrName/* or attrNameList */, element1, element2) {
function exchange(attrName) {
// 保存 element1 的属性,将 element2 的属性赋值给 element1, 再将保存的属性赋值给 element2
var tmp = element1.getAttribute(attrName)
element1.setAttribute(attrName, element2.getAttribute(attrName))
element2.setAttribute(attrName, tmp)
}
if (typeof attrName === "string") {
exchange(attrName)
} else if (typeof attrName === "object") {
for (let i = 0; i < attrName.length; i++) {
exchange(attrName[i])
}
}
}
swapElementInnerHTML(element1, element2) {
var tmp = element1.innerHTML
element1.innerHTML = element2.innerHTML
element2.innerHTML = tmp
}
swapElementsAttr(elementPairList) {
// [
// [attrName or attrNameList, element1, element2],
// [attrName or attrNameList, element1, element2],
// ...
// ]
for (let i = 0; i < elementPairList.length; i++) {
let elementPair = elementPairList[i]
this.swapElementAttr(elementPair[0], elementPair[1], elementPair[2])
}
}
// 交换数组元素
swapLinkedListItems(id, [fromIndex, toIndex]) {
if (fromIndex < toIndex)
[fromIndex, toIndex] = [toIndex, fromIndex]
var settings = this.workSpace.settings
let linkedList = document.getElementById(id)
let customAttr = linkedList.customAttr
// console.log(customAttr)
var gList = linkedList.childNodes
let from = gList[fromIndex]
let to = gList[toIndex]
var deltaX = customAttr.oneUnit * (fromIndex - toIndex);
var deltaY = customAttr.oneUnit * 1.08
var animateSettings = {
duration: 0.2,
ease: "sine.inOut",
delay: 0,
yoyo: true,
}
// 如果是相邻的两个元素交换,或者要交换的两个元素是同一个元素
if (Math.abs(fromIndex - toIndex) <= 1) {
deltaY /= 2
animateSettings.duration = 0.25
}
var that = this
let timeline = gsap.timeline({
onComplete: function () {
// 交换DOM元素中的值
that.swapElementInnerHTML(from.childNodes[1], to.childNodes[1])
// 交换 node 列表中的值
// console.log(customAttr.nodes)
let tmp = customAttr.nodes[fromIndex]
customAttr.nodes[fromIndex] = customAttr.nodes[toIndex]
customAttr.nodes[toIndex] = tmp
// console.log(customAttr.nodes)
updateConsole("当前数组为:" + customAttr.nodes.toString().replaceAll(",", ",&nbsp;&nbsp;&nbsp;"))
console.log("done")
}
}).add([
gsap.to(from.childNodes[0], { ...animateSettings, fill: settings.colorMap["fill_focus"] }),
gsap.to(to.childNodes[0], { ...animateSettings, fill: settings.colorMap["fill_focus"] }),
gsap.to(from.childNodes[1], { ...animateSettings, fill: settings.colorMap["text_focus"] }),
gsap.to(to.childNodes[1], { ...animateSettings, fill: settings.colorMap["text_focus"] }),
gsap.to(from.childNodes, { ...animateSettings, y: -deltaY }),
gsap.to(to.childNodes, { ...animateSettings, y: deltaY }),
]).add([
gsap.to(from.childNodes, { ...animateSettings, x: -deltaX }),
gsap.to(to.childNodes, { ...animateSettings, x: deltaX }),
]).add([
gsap.to(from.childNodes[0], { ...animateSettings, fill: settings.colorMap["fill"] }),
gsap.to(to.childNodes[0], { ...animateSettings, fill: settings.colorMap["fill"] }),
gsap.to(from.childNodes[1], { ...animateSettings, fill: settings.colorMap["text"] }),
gsap.to(to.childNodes[1], { ...animateSettings, fill: settings.colorMap["text"] }),
gsap.to(from.childNodes, { ...animateSettings, y: 0 }),
gsap.to(to.childNodes, { ...animateSettings, y: 0 }),
]).add([
// 恢复到动画之前的状态
gsap.to(from.childNodes, { ...animateSettings, duration: 0, x: 0, y: 0 }),
gsap.to(to.childNodes, { ...animateSettings, duration: 0, x: 0, y: 0 }),
])
customAttr.gsapTimeline.add(timeline)
}
// 高亮数组元素
highlightLinkedListItems(id, indexList) {
let linkedList = document.getElementById(id)
let customAttr = linkedList.customAttr
var gList = linkedList.childNodes
if (typeof (indexList) === "number")
indexList = [indexList]
var hightlightElementList_fill = []
var hightlightElementList_text = []
for (let i = 0; i < indexList.length; i++) {
const index = indexList[i];
hightlightElementList_fill.push(gList[index].childNodes[0])
hightlightElementList_text.push(gList[index].childNodes[1])
}
var animateSettings = {
duration: 0.2,
ease: "sine.inOut",
delay: 0,
yoyo: true,
}
let timeline = gsap.timeline({
onComplete: function () {
console.log("hightlight done")
}
}).add([
gsap.to(hightlightElementList_fill, { ...animateSettings, fill: settings.colorMap["fill_focus"] }),
gsap.to(hightlightElementList_text, { ...animateSettings, fill: settings.colorMap["text_focus"] }),
]).add([
gsap.to(hightlightElementList_fill, { ...animateSettings, fill: settings.colorMap["fill"] }),
gsap.to(hightlightElementList_text, { ...animateSettings, fill: settings.colorMap["text"] }),
])
customAttr.gsapTimeline.add(timeline)
}
}

166
src/assets/js/index.js Normal file
View File

@@ -0,0 +1,166 @@
// 全局变量
var settings = {
container: d3.select("#container"),
outerSize: { // 不带单位
width: 800,
height: 600
},
innerSize: { // 不带单位,通过 outerSize 和 margin 计算得到
width: null,
height: null
},
width: null, // 带单位,通过 outerSize 计算得到
height: null, // 带单位,通过 outerSize 计算得到
margin: { // 不带单位
top: 180,
right: 20,
bottom: 180,
left: 20
},
oneUnit: { // 不带单位,通过 innerSize 计算得到
transverse: null, // 横向单位长度 ( innerSize 的 1% )
longitudinal: null // 纵向单位长度 ( innerSize 的 1% )
},
colorMap: {
"background": "#ddd",
"stroke": "#e02433", // 边框颜色
"fill": "#ffa4ab", // 填充颜色
"fill_focus": "#ee737d", // 填充颜色
"text": "blue", // 文本颜色
"text_focus": "white", // 文本颜色
"red": "#f00",
"green": "#0f0",
},
animation: {
duration: 2000, // 不带单位,单位是毫秒
easing: "ease-in-out"
}
}
settings.width = settings.outerSize.width + "px";
settings.height = settings.outerSize.height + "px";
settings.innerSize.width = settings.outerSize.width - settings.margin.left - settings.margin.right;
settings.innerSize.height = settings.outerSize.height - settings.margin.top - settings.margin.bottom;
settings.oneUnit.transverse = settings.innerSize.width / 100;
settings.oneUnit.longitudinal = settings.innerSize.height / 100;
console.log("settings", settings)
// 初始化
var workSpace = new WorkSpace(settings)
var shape = new Shape(workSpace)
var animation = new VectorAnimation(workSpace)
function initialize() {
var elementId = "ele8"
d3.select("#btn_qucikSort").on("click", function () {
// 快速排序算法
var sortAlgorithm = new QuickSort(animation)
sortAlgorithm.doSortWithAnimation(elementId)
})
d3.select("#btn_bubbleSort").on("click", function () {
// 冒泡排序算法
var sortAlgorithm = new BubbleSort(animation)
sortAlgorithm.doSortWithAnimation(elementId)
})
}
initialize()
// 临时输出
function updateConsole(data) {
document.getElementById("console-div").innerHTML = data
}
// 测试
// shape.rectangle("ele1", {
// x: 10,
// y: 10,
// width: "20%",
// height: "30%",
// fillColor: "blue",
// strokeColor: "red"
// }).style("stroke-width", "10px")
// shape.circle("ele2", {
// x: 200,
// y: 50,
// radius: "20px",
// fillColor: "green",
// strokeColor: "red"
// })
// shape.text("ele3", {
// x: 200,
// y: 150,
// text: "文本",
// fillColor: "red"
// })
// shape.line("ele4", {
// x1: 100,
// y1: 100,
// x2: 200,
// y2: 200,
// strokeColor: "red"
// })
// var data = []
// for (let i = 0; i < 20; i++) // 生成随机数
// data.push(Math.ceil(Math.random() * 10))
// var scale = {
// x: d3.scaleLinear()
// .domain([0, data.length - 1])
// .range([0, settings.innerSize.width]),
// y: d3.scaleLinear()
// .domain([0, d3.max(data)])
// .range([settings.innerSize.height, 0])
// }
// var line_generator = d3.line()
// .x(function (d, i) { return scale.x(i); })
// .y(function (d, i) { return scale.y(d); })
// // .curve(d3.curveBasis)
// // .curve(d3.curveCardinal)
// shape.path("ele5", line_generator(data), {
// fillColor: "none",
// strokeColor: "red"
// })
// shape.path("ele6", line_generator.curve(d3.curveCardinal)(data), {
// fillColor: "none",
// strokeColor: "green"
// })
// // shape.addShape("g", "axisX").attr("transform", `translate(${settings.margin.left}, ${settings.outerSize.height - settings.margin.bottom})`)//.attr("fill", "red")
// shape.axis("axisX", {
// transform: `translate(${settings.margin.left}, ${settings.outerSize.height - settings.margin.bottom})`,
// axis: d3.axisBottom(scale.x)
// })
// shape.axis("axisY", {
// transform: `translate(${settings.margin.left}, ${settings.margin.top})`,
// axis: d3.axisLeft(scale.y)
// })
// shape.addNode("ele7", 300, 200, "20px", "20px", "文本")
var listData = [47, 11, 50, 13, 16, 49, 8, 9, 38, 27, 20] // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]
let fragment = shape.getLinkedListFragment("ele8", listData, {
x: 100,
y: 100,
width: "100px",
height: "100px",
})
console.log(fragment)
workSpace.primaryCanvas.node().appendChild(fragment)
document.getElementById("ele8").customAttr = fragment.customAttr
function getRandom(length) {
return Math.floor(Math.random() * length); // 可均衡获取 0 到 length-1 的随机整数。
}
// for (let i = 0; i < 10; i++) {
// var id = "ele8"
// animation.swapLinkedListItems(id, [getRandom(listData.length), getRandom(listData.length)])
// }

20375
src/assets/lib/d3/7.4.4/d3.js vendored Normal file

File diff suppressed because it is too large Load Diff

2
src/assets/lib/d3/7.4.4/d3.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,11 @@
/*!
* CSSRulePlugin 3.10.4
* https://greensock.com
*
* @license Copyright 2022, GreenSock. All rights reserved.
* Subject to the terms at https://greensock.com/standard-license or for Club GreenSock members, the agreement issued with that membership.
* @author: Jack Doyle, jack@greensock.com
*/
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e=e||self).window=e.window||{})}(this,function(e){"use strict";function h(){return"undefined"!=typeof window}function i(){return t||h()&&(t=window.gsap)&&t.registerPlugin&&t}function j(){return n||(s(),o||console.warn("Please gsap.registerPlugin(CSSPlugin, CSSRulePlugin)")),n}var t,n,c,o,s=function _initCore(e){t=e||i(),h()&&(c=document),t&&(o=t.plugins.css)&&(n=1)},r={version:"3.10.4",name:"cssRule",init:function init(e,t,n,i,s){if(!j()||void 0===e.cssText)return!1;var r=e._gsProxy=e._gsProxy||c.createElement("div");this.ss=e,this.style=r.style,r.style.cssText=e.cssText,o.prototype.init.call(this,r,t,n,i,s)},render:function render(e,t){for(var n,i=t._pt,s=t.style,r=t.ss;i;)i.r(e,i.d),i=i._next;for(n=s.length;-1<--n;)r[s[n]]=s[s[n]]},getRule:function getRule(e){j();var t,n,i,s,r=c.all?"rules":"cssRules",o=c.styleSheets,l=o.length,u=":"===e.charAt(0);for(e=(u?"":",")+e.split("::").join(":").toLowerCase()+",",u&&(s=[]);l--;){try{if(!(n=o[l][r]))continue;t=n.length}catch(e){console.warn(e);continue}for(;-1<--t;)if((i=n[t]).selectorText&&-1!==(","+i.selectorText.split("::").join(":").toLowerCase()+",").indexOf(e)){if(!u)return i.style;s.push(i.style)}}return s},register:s};i()&&t.registerPlugin(r),e.CSSRulePlugin=r,e.default=r;if (typeof(window)==="undefined"||window!==e){Object.defineProperty(e,"__esModule",{value:!0})} else {delete e.default}});

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,11 @@
/*!
* EasePack 3.10.4
* https://greensock.com
*
* @license Copyright 2022, GreenSock. All rights reserved.
* Subject to the terms at https://greensock.com/standard-license or for Club GreenSock members, the agreement issued with that membership.
* @author: Jack Doyle, jack@greensock.com
*/
!function(e,n){"object"==typeof exports&&"undefined"!=typeof module?n(exports):"function"==typeof define&&define.amd?define(["exports"],n):n((e=e||self).window=e.window||{})}(this,function(e){"use strict";function f(){return w||"undefined"!=typeof window&&(w=window.gsap)&&w.registerPlugin&&w}function g(e,n){return!!(void 0===e?n:e&&!~(e+"").indexOf("false"))}function h(e){if(w=e||f()){r=w.registerEase;var n,t=w.parseEase(),o=function createConfig(t){return function(e){var n=.5+e/2;t.config=function(e){return t(2*(1-e)*e*n+e*e)}}};for(n in t)t[n].config||o(t[n]);for(n in r("slow",a),r("expoScale",s),r("rough",u),c)"version"!==n&&w.core.globals(n,c[n])}}function i(e,n,t){var o=(e=Math.min(1,e||.7))<1?n||0===n?n:.7:0,r=(1-e)/2,i=r+e,a=g(t);return function(e){var n=e+(.5-e)*o;return e<r?a?1-(e=1-e/r)*e:n-(e=1-e/r)*e*e*e*n:i<e?a?1===e?0:1-(e=(e-i)/r)*e:n+(e-n)*(e=(e-i)/r)*e*e*e:a?1:n}}function j(n,e,t){var o=Math.log(e/n),r=e-n;return t=t&&w.parseEase(t),function(e){return(n*Math.exp(o*(t?t(e):e))-n)/r}}function k(e,n,t){this.t=e,this.v=n,t&&(((this.next=t).prev=this).c=t.v-n,this.gap=t.t-e)}function l(e){"object"!=typeof e&&(e={points:+e||20});for(var n,t,o,r,i,a,f,s=e.taper||"none",u=[],c=0,p=0|(+e.points||20),l=p,v=g(e.randomize,!0),d=g(e.clamp),h=w?w.parseEase(e.template):0,x=.4*(+e.strength||1);-1<--l;)n=v?Math.random():1/p*l,t=h?h(n):n,o="none"===s?x:"out"===s?(r=1-n)*r*x:"in"===s?n*n*x:n<.5?(r=2*n)*r*.5*x:(r=2*(1-n))*r*.5*x,v?t+=Math.random()*o-.5*o:l%2?t+=.5*o:t-=.5*o,d&&(1<t?t=1:t<0&&(t=0)),u[c++]={x:n,y:t};for(u.sort(function(e,n){return e.x-n.x}),a=new k(1,1,null),l=p;l--;)i=u[l],a=new k(i.x,i.y,a);return f=new k(0,0,a.t?a:a.next),function(e){var n=f;if(e>n.t){for(;n.next&&e>=n.t;)n=n.next;n=n.prev}else for(;n.prev&&e<=n.t;)n=n.prev;return(f=n).v+(e-n.t)/n.gap*n.c}}var w,r,a=i(.7);(a.ease=a).config=i;var s=j(1,2);s.config=j;var u=l();(u.ease=u).config=l;var c={SlowMo:a,RoughEase:u,ExpoScaleEase:s};for(var n in c)c[n].register=h,c[n].version="3.10.4";f()&&w.registerPlugin(a),e.EasePack=c,e.ExpoScaleEase=s,e.RoughEase=u,e.SlowMo=a,e.default=c;if (typeof(window)==="undefined"||window!==e){Object.defineProperty(e,"__esModule",{value:!0})} else {delete e.default}});

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,11 @@
/*!
* EaselPlugin 3.10.4
* https://greensock.com
*
* @license Copyright 2022, GreenSock. All rights reserved.
* Subject to the terms at https://greensock.com/standard-license or for Club GreenSock members, the agreement issued with that membership.
* @author: Jack Doyle, jack@greensock.com
*/
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e=e||self).window=e.window||{})}(this,function(e){"use strict";function k(){return"undefined"!=typeof window}function l(){return h||k()&&(h=window.gsap)&&h.registerPlugin&&h}function m(){return r||t&&t.createjs||t||{}}function n(e){return console.warn(e)}function o(e){var t=e.getBounds&&e.getBounds();t||(t=e.nominalBounds||{x:0,y:0,width:100,height:100},e.setBounds&&e.setBounds(t.x,t.y,t.width,t.height)),e.cache&&e.cache(t.x,t.y,t.width,t.height),n("EaselPlugin: for filters to display in EaselJS, you must call the object's cache() method first. GSAP attempted to use the target's getBounds() for the cache but that may not be completely accurate. "+e)}function p(e,t,r){(b=b||m().ColorFilter)||n("EaselPlugin error: The EaselJS ColorFilter JavaScript file wasn't loaded.");for(var i,l,s,u,a,f,c=e.filters||[],d=c.length;d--;)if(c[d]instanceof b){l=c[d];break}if(l||(l=new b,c.push(l),e.filters=c),s=l.clone(),null!=t.tint)i=h.utils.splitColor(t.tint),u=null!=t.tintAmount?+t.tintAmount:1,s.redOffset=i[0]*u,s.greenOffset=i[1]*u,s.blueOffset=i[2]*u,s.redMultiplier=s.greenMultiplier=s.blueMultiplier=1-u;else for(a in t)"exposure"!==a&&"brightness"!==a&&(s[a]=+t[a]);for(null!=t.exposure?(s.redOffset=s.greenOffset=s.blueOffset=255*(t.exposure-1),s.redMultiplier=s.greenMultiplier=s.blueMultiplier=1):null!=t.brightness&&(u=t.brightness-1,s.redOffset=s.greenOffset=s.blueOffset=0<u?255*u:0,s.redMultiplier=s.greenMultiplier=s.blueMultiplier=1-Math.abs(u)),d=8;d--;)l[a=M[d]]!==s[a]&&(f=r.add(l,a,l[a],s[a]))&&(f.op="easel_colorFilter");r._props.push("easel_colorFilter"),e.cacheID||o(e)}function u(e,t){if(!(e instanceof Array&&t instanceof Array))return t;var r,i,n=[],l=0,o=0;for(r=0;r<4;r++){for(i=0;i<5;i++)o=4===i?e[l+4]:0,n[l+i]=e[l]*t[i]+e[l+1]*t[i+5]+e[l+2]*t[i+10]+e[l+3]*t[i+15]+o;l+=5}return n}function z(e,t,r){(d=d||m().ColorMatrixFilter)||n("EaselPlugin: The EaselJS ColorMatrixFilter JavaScript file wasn't loaded.");for(var i,l,s,a,f=e.filters||[],c=f.length;-1<--c;)if(f[c]instanceof d){s=f[c];break}for(s||(s=new d(w.slice()),f.push(s),e.filters=f),l=s.matrix,i=w.slice(),null!=t.colorize&&(i=function _colorize(e,t,r){isNaN(r)&&(r=1);var i=h.utils.splitColor(t),n=i[0]/255,l=i[1]/255,o=i[2]/255,s=1-r;return u([s+r*n*x,r*n*y,r*n*_,0,0,r*l*x,s+r*l*y,r*l*_,0,0,r*o*x,r*o*y,s+r*o*_,0,0,0,0,0,1,0],e)}(i,t.colorize,Number(t.colorizeAmount))),null!=t.contrast&&(i=function _setContrast(e,t){return isNaN(t)?e:u([t+=.01,0,0,0,128*(1-t),0,t,0,0,128*(1-t),0,0,t,0,128*(1-t),0,0,0,1,0],e)}(i,Number(t.contrast))),null!=t.hue&&(i=function _setHue(e,t){if(isNaN(t))return e;t*=Math.PI/180;var r=Math.cos(t),i=Math.sin(t);return u([x+r*(1-x)+i*-x,y+r*-y+i*-y,_+r*-_+i*(1-_),0,0,x+r*-x+.143*i,y+r*(1-y)+.14*i,_+r*-_+-.283*i,0,0,x+r*-x+i*-(1-x),y+r*-y+i*y,_+r*(1-_)+i*_,0,0,0,0,0,1,0,0,0,0,0,1],e)}(i,Number(t.hue))),null!=t.saturation&&(i=function _setSaturation(e,t){if(isNaN(t))return e;var r=1-t,i=r*x,n=r*y,l=r*_;return u([i+t,n,l,0,0,i,n+t,l,0,0,i,n,l+t,0,0,0,0,0,1,0],e)}(i,Number(t.saturation))),c=i.length;-1<--c;)i[c]!==l[c]&&(a=r.add(l,c,l[c],i[c]))&&(a.op="easel_colorMatrixFilter");r._props.push("easel_colorMatrixFilter"),e.cacheID||o(),r._matrix=l}function A(e){h=e||l(),k()&&(t=window),h&&(g=1)}var h,g,t,r,b,d,M="redMultiplier,greenMultiplier,blueMultiplier,alphaMultiplier,redOffset,greenOffset,blueOffset,alphaOffset".split(","),w=[1,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,1,0],x=.212671,y=.71516,_=.072169,i={version:"3.10.4",name:"easel",init:function init(e,t,r,i,l){var o,s,u,a,f,c,d;for(o in g||(A(),h||n("Please gsap.registerPlugin(EaselPlugin)")),this.target=e,t)if(f=t[o],"colorFilter"===o||"tint"===o||"tintAmount"===o||"exposure"===o||"brightness"===o)u||(p(e,t.colorFilter||t,this),u=!0);else if("saturation"===o||"contrast"===o||"hue"===o||"colorize"===o||"colorizeAmount"===o)a||(z(e,t.colorMatrixFilter||t,this),a=!0);else if("frame"===o){if("string"==typeof f&&"="!==f.charAt(1)&&(c=e.labels))for(d=0;d<c.length;d++)c[d].label===f&&(f=c[d].position);(s=this.add(e,"gotoAndStop",e.currentFrame,f,i,l,Math.round))&&(s.op=o)}else null!=e[o]&&this.add(e,o,"get",f)},render:function render(e,t){for(var r=t._pt;r;)r.r(e,r.d),r=r._next;t.target.cacheID&&t.target.updateCache()},register:A,registerCreateJS:function(e){r=e}};l()&&h.registerPlugin(i),e.EaselPlugin=i,e.default=i;if (typeof(window)==="undefined"||window!==e){Object.defineProperty(e,"__esModule",{value:!0})} else {delete e.default}});

File diff suppressed because one or more lines are too long

11
src/assets/lib/gsap/3.10.4/Flip.min.js vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,11 @@
/*!
* ScrollToPlugin 3.10.4
* https://greensock.com
*
* @license Copyright 2022, GreenSock. All rights reserved.
* Subject to the terms at https://greensock.com/standard-license or for Club GreenSock members, the agreement issued with that membership.
* @author: Jack Doyle, jack@greensock.com
*/
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e=e||self).window=e.window||{})}(this,function(e){"use strict";function k(){return"undefined"!=typeof window}function l(){return u||k()&&(u=window.gsap)&&u.registerPlugin&&u}function m(e){return"string"==typeof e}function n(e){return"function"==typeof e}function o(e,t){var o="x"===t?"Width":"Height",n="scroll"+o,r="client"+o;return e===T||e===i||e===c?Math.max(i[n],c[n])-(T["inner"+o]||i[r]||c[r]):e[n]-e["offset"+o]}function p(e,t){var o="scroll"+("x"===t?"Left":"Top");return e===T&&(null!=e.pageXOffset?o="page"+t.toUpperCase()+"Offset":e=null!=i[o]?i:c),function(){return e[o]}}function r(e,t){if(!(e=f(e)[0])||!e.getBoundingClientRect)return console.warn("scrollTo target doesn't exist. Using 0")||{x:0,y:0};var o=e.getBoundingClientRect(),n=!t||t===T||t===c,r=n?{top:i.clientTop-(T.pageYOffset||i.scrollTop||c.scrollTop||0),left:i.clientLeft-(T.pageXOffset||i.scrollLeft||c.scrollLeft||0)}:t.getBoundingClientRect(),l={x:o.left-r.left,y:o.top-r.top};return!n&&t&&(l.x+=p(t,"x")(),l.y+=p(t,"y")()),l}function s(e,t,n,l,i){return isNaN(e)||"object"==typeof e?m(e)&&"="===e.charAt(1)?parseFloat(e.substr(2))*("-"===e.charAt(0)?-1:1)+l-i:"max"===e?o(t,n)-i:Math.min(o(t,n),r(e,t)[n]-i):parseFloat(e)-i}function t(){u=l(),k()&&u&&document.body&&(T=window,c=document.body,i=document.documentElement,f=u.utils.toArray,u.config({autoKillThreshold:7}),v=u.config(),a=1)}var u,a,T,i,c,f,v,y={version:"3.10.4",name:"scrollTo",rawVars:1,register:function register(e){u=e,t()},init:function init(e,o,r,l,i){a||t();var c=this,f=u.getProperty(e,"scrollSnapType");c.isWin=e===T,c.target=e,c.tween=r,o=function _clean(e,t,o,r){if(n(e)&&(e=e(t,o,r)),"object"!=typeof e)return m(e)&&"max"!==e&&"="!==e.charAt(1)?{x:e,y:e}:{y:e};if(e.nodeType)return{y:e,x:e};var l,i={};for(l in e)i[l]="onAutoKill"!==l&&n(e[l])?e[l](t,o,r):e[l];return i}(o,l,e,i),c.vars=o,c.autoKill=!!o.autoKill,c.getX=p(e,"x"),c.getY=p(e,"y"),c.x=c.xPrev=c.getX(),c.y=c.yPrev=c.getY(),f&&"none"!==f&&(c.snap=1,c.snapInline=e.style.scrollSnapType,e.style.scrollSnapType="none"),null!=o.x?(c.add(c,"x",c.x,s(o.x,e,"x",c.x,o.offsetX||0),l,i),c._props.push("scrollTo_x")):c.skipX=1,null!=o.y?(c.add(c,"y",c.y,s(o.y,e,"y",c.y,o.offsetY||0),l,i),c._props.push("scrollTo_y")):c.skipY=1},render:function render(e,t){for(var n,r,l,i,s,p=t._pt,c=t.target,f=t.tween,u=t.autoKill,a=t.xPrev,y=t.yPrev,d=t.isWin,x=t.snap,g=t.snapInline;p;)p.r(e,p.d),p=p._next;n=d||!t.skipX?t.getX():a,l=(r=d||!t.skipY?t.getY():y)-y,i=n-a,s=v.autoKillThreshold,t.x<0&&(t.x=0),t.y<0&&(t.y=0),u&&(!t.skipX&&(s<i||i<-s)&&n<o(c,"x")&&(t.skipX=1),!t.skipY&&(s<l||l<-s)&&r<o(c,"y")&&(t.skipY=1),t.skipX&&t.skipY&&(f.kill(),t.vars.onAutoKill&&t.vars.onAutoKill.apply(f,t.vars.onAutoKillParams||[]))),d?T.scrollTo(t.skipX?n:t.x,t.skipY?r:t.y):(t.skipY||(c.scrollTop=t.y),t.skipX||(c.scrollLeft=t.x)),!x||1!==e&&0!==e||(r=c.scrollTop,n=c.scrollLeft,g?c.style.scrollSnapType=g:c.style.removeProperty("scroll-snap-type"),c.scrollTop=r+1,c.scrollLeft=n+1,c.scrollTop=r,c.scrollLeft=n),t.xPrev=t.x,t.yPrev=t.y},kill:function kill(e){var t="scrollTo"===e;!t&&"scrollTo_x"!==e||(this.skipX=1),!t&&"scrollTo_y"!==e||(this.skipY=1)}};y.max=o,y.getOffset=r,y.buildGetter=p,l()&&u.registerPlugin(y),e.ScrollToPlugin=y,e.default=y;if (typeof(window)==="undefined"||window!==e){Object.defineProperty(e,"__esModule",{value:!0})} else {delete e.default}});

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

11
src/assets/lib/gsap/3.10.4/gsap.min.js vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

91
src/index.html Normal file
View File

@@ -0,0 +1,91 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>小墨 | 算法可视化 | Algorithm Visualization</title>
<!-- 通过 webpack 引入 -->
<!-- <link rel="stylesheet" href="./assets/css/index.css"> -->
<style>
/* 调整 SVG 中文本定位点到文本中央 */
text {
text-anchor: middle;
dominant-baseline: middle;
}
svg {
user-select: none;
}
</style>
</head>
<body>
<div class="main">
<div id="container" class="container">
正在加载中,请稍候...
</div>
<div style="display: grid; place-items: center;">
<div id="control-div">
<button id="btn_qucikSort">快速排序</button>
<button id="btn_bubbleSort">冒泡排序</button>
</div>
<div id="console-div"></div>
</div>
<!-- <div class="header">
<h1>小墨 | 算法可视化</h1>
</div>
<div class="content">
<div class="content-left">
<div class="content-left-header">
<h2>算法可视化</h2>
</div>
<div class="content-left-body">
<ul>
<li><a href="">算法可视化</a></li>
</ul>
</div>
</div>
<div class="content-right">
<div class="content-right-header">
<h2>算法可视化</h2>
</div>
<div class="content-right-body">
<div class="content-right-body-header">
<h3>算法可视化</h3>
</div>
<div class="content-right-body-body">
<div id="container" class="container">
</div>
</div>
</div>
</div>
</div>
<div class="footer">
<p>
<a href="">算法可视化</a>
</p>
</div> -->
</div>
<!-- D3.js refer: https://d3js.org/ -->
<script src="./assets/lib/d3/7.4.4/d3.min.js"></script>
<!-- GSAP refer: https://greensock.com/docs/v3/Installation -->
<script src="./assets/lib/gsap/3.10.4/gsap.min.js"></script>
<!-- class -->
<script src="./assets/js/class.js"></script>
<!-- 排序算法 -->
<script src="./assets/js/algorithm/sort.js"></script>
<script src="./assets/js/index.js"></script>
<!-- 算法测试 -->
<!-- <script src="./assets/js/algorithm/test.js"></script> -->
</body>
</html>

8
src/webpack.js Normal file
View File

@@ -0,0 +1,8 @@
// 引入html
import './index.html'
// 引入样式资源
import './assets/css/index.css'
//
// import "./assets/lib/d3/7.4.4/d3.min.js"