1

I am trying to make a Node animation like this for SwiftUI --> https://oguzhaneroglu.com/projects/nodes.js/

That link has the source code (which is literally a small file) which I am converting.

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 () {

I assume the contents in that function causes the animation to loop. Can anyone give me any advice on how I can do this?

Just a coder
  • 15,480
  • 16
  • 85
  • 138

2 Answers2

1

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 () {

Actually this is equivalent of .onAppear modifier on root view, so if original JS code activates animation in function(), then in SwiftUI we would do it as for example

struct ContentView: View {
    
    var body: some View {
       VStack {            // << any view or container
          // ... other subviews
       }
       .onAppear(perform: function)
    }

    func function() {
        // ... any needed manipulations
    }
}
Asperi
  • 228,894
  • 20
  • 464
  • 690
  • ..I know the original question was about the event listener function... but when I added the bounty for this question I wanted a little more help in creating this... For instance, I do not know how to make it continuous loop.. I gave a +1 for help.. but your help is very much needed :( – swift nub Dec 15 '20 at 06:17
  • You need animation with `.repeatForever(...)` modifier. Without your code it is hard to say anything specific but next topics contain good examples where/how to use it https://stackoverflow.com/a/64956536/12299030, https://stackoverflow.com/a/64566746/12299030, https://stackoverflow.com/a/61778318/12299030. And you can find more by `swiftui+repeatForever` keywords. – Asperi Dec 15 '20 at 06:31
  • The code is me basically converting [this](https://github.com/rohanrhu/nodes.js/blob/master/js/nodes.js) to swift. Its only a few lines of code because i don't need the mouse function starting on line 71. I will look at the examples you posted, and then check back here in the morning. – swift nub Dec 15 '20 at 06:46
  • After looking at it closely, I don't think it can be done in SwiftUI. It Probably can be done with UIkit. Or if it can be done, It is not something I can do for my level. I will award you the points once stack overflow allows me to do so. thanks again. – swift nub Dec 15 '20 at 09:04
  • @Asperi That was it. I was thinking in the wrong direction! – Just a coder Dec 15 '20 at 23:40
  • @swiftnub I posted an answer for you if it works. – Just a coder Dec 15 '20 at 23:40
-1

For my question, this HAD to be done in Swift. If this is not a strict requirement for you, then here is what you can do.

  1. Put this inside a node.html file.
<!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>
  1. Put this inside a node.js file
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();
    });
});
  1. Add those 2 files to your project, then create a 3rd file called 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)
    }
    
}

Finally, add that WebView to what ever view you need on your iOS app. You now have the ability to do fancy animations of any html animations out there without knowing a lick of javascript.

Let me know if this works for you.

Just a coder
  • 15,480
  • 16
  • 85
  • 138