如何在 SwiftUI 中制作节点动画?
How to make an animation of Nodes in SwiftUI?
我正在尝试为 SwiftUI 制作这样的节点动画 --> https://oguzhaneroglu.com/projects/nodes.js/
那个 link 有我正在转换的源代码(实际上是一个小文件)。
我感到困惑的部分是,这个功能在 SwiftUI 中的等价物是什么?
window[window.addEventListener ? 'addEventListener': 'attachEvent']
(window.addEventListener ? 'load': 'onload', function () {
我假设该函数中的内容会导致动画循环。
任何人都可以就如何做到这一点给我任何建议吗?
The part I am confused on is, what is the equivalent in SwiftUI for
this function?
window[window.addEventListener ? 'addEventListener': 'attachEvent']
(window.addEventListener ? 'load': 'onload', function () {
实际上这相当于根视图上的 .onAppear
修饰符,所以如果原始 JS 代码在 function()
中激活动画,那么在 SwiftUI 中我们会像
那样做
struct ContentView: View {
var body: some View {
VStack { // << any view or container
// ... other subviews
}
.onAppear(perform: function)
}
func function() {
// ... any needed manipulations
}
}
对于我的问题,这必须在 Swift 中完成。如果这对您来说不是一个严格的要求,那么您可以这样做。
- 将其放入
node.html
文件中。
<!DOCTYPE html>
<html>
<body>
<script type="text/javascript" src="nodes.js"></script>
<script type="text/javascript">
var nodesjs = new NodesJs({
id: 'nodes',
width: window.innerWidth,
height: window.innerHeight,
particleSize: 4,
lineSize: 2,
particleColor: [85, 85, 121],
lineColor: [255, 255, 255],
backgroundDuration: 4000,
number: window.hasOwnProperty('orientation') ? 30: 100,
speed: 15
});
</script>
<div style="position: absolute; left: 0px; top: 0px; overflow: hidden; width: 100%; height: 100%;">
<canvas id="nodes"></canvas>
</div>
</body>
</html>
- 将其放入
node.js
文件
var NodesJs = (function (parameters) {
t_NodesJs = this;
t_NodesJs.id = parameters.id;
t_NodesJs.width = parameters.width;
t_NodesJs.height = parameters.height;
t_NodesJs.particleSize = parameters.particleSize ? parameters.particleSize: 2;
t_NodesJs.lineSize = parameters.lineSize ? parameters.lineSize: 1;
t_NodesJs.particleColor = parameters.particleColor ? 'rgba('+parameters.particleColor.join(',')+')': 'rgba(255,255,255,0.3)';
t_NodesJs.lineColor = parameters.lineColor ? parameters.lineColor: '255,255,255';
t_NodesJs.number = parameters.number ? parameters.number: 100;
t_NodesJs.speed = parameters.speed ? parameters.speed: 20;
var canvas;
var ctx;
var cw;
var ch;
var t0 = Date.now();
var dt = 0;
t_NodesJs.nodes = [];
t_NodesJs.setWidth = function (width) {
canvas.width = width;
cw = width;
};
t_NodesJs.setHeight = function (height) {
canvas.height = height;
ch = height;
};
t_NodesJs.placeNodes = function (number) {
t_NodesJs.nodes = [];
for (var i=0; i < number; i++) {
t_NodesJs.nodes.push([
Math.floor(Math.random() * (cw - 0 + 1)) + 0,
Math.floor(Math.random() * (ch - 0 + 1)) + 0,
Math.random() * (Math.PI * 2 - 0 + 1) + 0,
[]
]);
}
};
var isPositive = function (num) {
return num >= 0;
};
var isNetagive = function (num) {
return num <= -1;
};
window[window.addEventListener ? 'addEventListener': 'attachEvent']
(window.addEventListener ? 'load': 'onload', function () {
canvas = document.getElementById(t_NodesJs.id);
ctx = canvas.getContext('2d');
canvas.width = t_NodesJs.width;
canvas.height = t_NodesJs.height;
cw = canvas.width;
ch = canvas.height;
t_NodesJs.placeNodes(t_NodesJs.number);
var step = function () {
window.requestAnimationFrame(step);
ctx.clearRect(0, 0, cw, ch);
// Fill the background
ctx.beginPath();
ctx.fillStyle = 'rgb('+0+', '+0+', '+46+')';
ctx.fillRect(0, 0, cw, ch);
ctx.fill();
t_NodesJs.nodes.forEach(function (_node, _node_i) {
_node[0] += Math.cos(_node[2]) * t_NodesJs.speed * (dt/1000.0);
_node[1] += Math.sin(_node[2]) * t_NodesJs.speed * (dt/1000.0);
if (_node[0] < 0) {
_node[0] = cw + (_node[0] % cw);
}
if (_node[0] > cw) {
_node[0] = _node[0] % cw;
}
if (_node[1] < 0) {
_node[1] = ch + (_node[1] % ch);
}
if (_node[1] > ch) {
_node[1] = _node[1] % ch;
}
ctx.fillStyle = t_NodesJs.particleColor;
ctx.beginPath();
ctx.arc(
_node[0],
_node[1],
t_NodesJs.particleSize,
0,
Math.PI * 2,
true
);
ctx.fill();
_node[3] = [];
t_NodesJs.nodes.forEach(function (_node2, _node2_i) {
if (_node_i == _node2_i) {
return true;
}
if (_node[3].indexOf(_node2_i) > -1) {
return true;
}
var dx = Math.abs(_node[0] - _node2[0]);
var dy = Math.abs(_node[1] - _node2[1]);
var d = Math.sqrt(Math.pow(dx, 2) + Math.pow(dy, 2));
var alpha = 0;
if (d <= 300) {
alpha = 0.3 - ((0.3 * d) / 200);
}
if (alpha == 0) {
return true;
}
_node2[3].push(_node_i);
ctx.strokeStyle = 'rgba('+t_NodesJs.lineColor+','+alpha+')';
ctx.lineWidth = t_NodesJs.lineSize;
ctx.beginPath();
ctx.moveTo(_node[0], _node[1]);
ctx.lineTo(_node2[0], _node2[1]);
ctx.stroke();
});
});
dt = Date.now() - t0;
t0 = Date.now();
};
step();
});
});
- 将这 2 个文件添加到您的项目中,然后创建名为
WebView.swift
的第三个文件
import SwiftUI
import WebKit
struct WebView : UIViewRepresentable {
func makeUIView(context: Context) -> WKWebView {
return WKWebView()
}
func updateUIView(_ uiView: WKWebView, context: Context) {
let url = Bundle.main.url(forResource: "node", withExtension: "html")!
uiView.loadFileURL(url, allowingReadAccessTo: url)
let request = URLRequest(url: url)
uiView.load(request)
}
}
最后,将 WebView
添加到您在 iOS 应用中需要的任何视图。您现在可以在不了解 javascript.
的情况下制作任何 html 动画的奇特动画
让我知道这是否适合你。
我正在尝试为 SwiftUI 制作这样的节点动画 --> https://oguzhaneroglu.com/projects/nodes.js/
那个 link 有我正在转换的源代码(实际上是一个小文件)。
我感到困惑的部分是,这个功能在 SwiftUI 中的等价物是什么?
window[window.addEventListener ? 'addEventListener': 'attachEvent']
(window.addEventListener ? 'load': 'onload', function () {
我假设该函数中的内容会导致动画循环。 任何人都可以就如何做到这一点给我任何建议吗?
The part I am confused on is, what is the equivalent in SwiftUI for this function?
window[window.addEventListener ? 'addEventListener': 'attachEvent'] (window.addEventListener ? 'load': 'onload', function () {
实际上这相当于根视图上的 .onAppear
修饰符,所以如果原始 JS 代码在 function()
中激活动画,那么在 SwiftUI 中我们会像
struct ContentView: View {
var body: some View {
VStack { // << any view or container
// ... other subviews
}
.onAppear(perform: function)
}
func function() {
// ... any needed manipulations
}
}
对于我的问题,这必须在 Swift 中完成。如果这对您来说不是一个严格的要求,那么您可以这样做。
- 将其放入
node.html
文件中。
<!DOCTYPE html>
<html>
<body>
<script type="text/javascript" src="nodes.js"></script>
<script type="text/javascript">
var nodesjs = new NodesJs({
id: 'nodes',
width: window.innerWidth,
height: window.innerHeight,
particleSize: 4,
lineSize: 2,
particleColor: [85, 85, 121],
lineColor: [255, 255, 255],
backgroundDuration: 4000,
number: window.hasOwnProperty('orientation') ? 30: 100,
speed: 15
});
</script>
<div style="position: absolute; left: 0px; top: 0px; overflow: hidden; width: 100%; height: 100%;">
<canvas id="nodes"></canvas>
</div>
</body>
</html>
- 将其放入
node.js
文件
var NodesJs = (function (parameters) {
t_NodesJs = this;
t_NodesJs.id = parameters.id;
t_NodesJs.width = parameters.width;
t_NodesJs.height = parameters.height;
t_NodesJs.particleSize = parameters.particleSize ? parameters.particleSize: 2;
t_NodesJs.lineSize = parameters.lineSize ? parameters.lineSize: 1;
t_NodesJs.particleColor = parameters.particleColor ? 'rgba('+parameters.particleColor.join(',')+')': 'rgba(255,255,255,0.3)';
t_NodesJs.lineColor = parameters.lineColor ? parameters.lineColor: '255,255,255';
t_NodesJs.number = parameters.number ? parameters.number: 100;
t_NodesJs.speed = parameters.speed ? parameters.speed: 20;
var canvas;
var ctx;
var cw;
var ch;
var t0 = Date.now();
var dt = 0;
t_NodesJs.nodes = [];
t_NodesJs.setWidth = function (width) {
canvas.width = width;
cw = width;
};
t_NodesJs.setHeight = function (height) {
canvas.height = height;
ch = height;
};
t_NodesJs.placeNodes = function (number) {
t_NodesJs.nodes = [];
for (var i=0; i < number; i++) {
t_NodesJs.nodes.push([
Math.floor(Math.random() * (cw - 0 + 1)) + 0,
Math.floor(Math.random() * (ch - 0 + 1)) + 0,
Math.random() * (Math.PI * 2 - 0 + 1) + 0,
[]
]);
}
};
var isPositive = function (num) {
return num >= 0;
};
var isNetagive = function (num) {
return num <= -1;
};
window[window.addEventListener ? 'addEventListener': 'attachEvent']
(window.addEventListener ? 'load': 'onload', function () {
canvas = document.getElementById(t_NodesJs.id);
ctx = canvas.getContext('2d');
canvas.width = t_NodesJs.width;
canvas.height = t_NodesJs.height;
cw = canvas.width;
ch = canvas.height;
t_NodesJs.placeNodes(t_NodesJs.number);
var step = function () {
window.requestAnimationFrame(step);
ctx.clearRect(0, 0, cw, ch);
// Fill the background
ctx.beginPath();
ctx.fillStyle = 'rgb('+0+', '+0+', '+46+')';
ctx.fillRect(0, 0, cw, ch);
ctx.fill();
t_NodesJs.nodes.forEach(function (_node, _node_i) {
_node[0] += Math.cos(_node[2]) * t_NodesJs.speed * (dt/1000.0);
_node[1] += Math.sin(_node[2]) * t_NodesJs.speed * (dt/1000.0);
if (_node[0] < 0) {
_node[0] = cw + (_node[0] % cw);
}
if (_node[0] > cw) {
_node[0] = _node[0] % cw;
}
if (_node[1] < 0) {
_node[1] = ch + (_node[1] % ch);
}
if (_node[1] > ch) {
_node[1] = _node[1] % ch;
}
ctx.fillStyle = t_NodesJs.particleColor;
ctx.beginPath();
ctx.arc(
_node[0],
_node[1],
t_NodesJs.particleSize,
0,
Math.PI * 2,
true
);
ctx.fill();
_node[3] = [];
t_NodesJs.nodes.forEach(function (_node2, _node2_i) {
if (_node_i == _node2_i) {
return true;
}
if (_node[3].indexOf(_node2_i) > -1) {
return true;
}
var dx = Math.abs(_node[0] - _node2[0]);
var dy = Math.abs(_node[1] - _node2[1]);
var d = Math.sqrt(Math.pow(dx, 2) + Math.pow(dy, 2));
var alpha = 0;
if (d <= 300) {
alpha = 0.3 - ((0.3 * d) / 200);
}
if (alpha == 0) {
return true;
}
_node2[3].push(_node_i);
ctx.strokeStyle = 'rgba('+t_NodesJs.lineColor+','+alpha+')';
ctx.lineWidth = t_NodesJs.lineSize;
ctx.beginPath();
ctx.moveTo(_node[0], _node[1]);
ctx.lineTo(_node2[0], _node2[1]);
ctx.stroke();
});
});
dt = Date.now() - t0;
t0 = Date.now();
};
step();
});
});
- 将这 2 个文件添加到您的项目中,然后创建名为
WebView.swift
的第三个文件
import SwiftUI
import WebKit
struct WebView : UIViewRepresentable {
func makeUIView(context: Context) -> WKWebView {
return WKWebView()
}
func updateUIView(_ uiView: WKWebView, context: Context) {
let url = Bundle.main.url(forResource: "node", withExtension: "html")!
uiView.loadFileURL(url, allowingReadAccessTo: url)
let request = URLRequest(url: url)
uiView.load(request)
}
}
最后,将 WebView
添加到您在 iOS 应用中需要的任何视图。您现在可以在不了解 javascript.
让我知道这是否适合你。