Logo

dev-resources.site

for different kinds of informations.

🚀Building a Multi-Step Loading Screen with Electron

Published at
12/4/2024
Categories
electron
javascript
webdev
frontend
Author
blamsa0mine
Author
11 person written this
blamsa0mine
open
🚀Building a Multi-Step Loading Screen with Electron

Building a Multi-Step Loading Screen with Electron

In this article, I'll walk you through how I built an Electron application with a multi-step loading screen. We'll cover everything from setting up the main Electron process to designing the splash screen and login page.

Why Electron?

Electron allows us to build cross-platform desktop apps with web technologies like HTML, CSS, and JavaScript. It provides the tools to create powerful user experiences, such as loading screens and transitions, while keeping the code simple.


Setting up the Main Process

The heart of any Electron app lies in its main.js. Here's how we set up the main process to create a splash screen, load the main window, and simulate a multi-step loading process:

// Modules to control application life and create native browser window
const { app, BrowserWindow } = require('electron')
const path = require('node:path')
const EventEmitter = require('events')

const loadingEvents = new EventEmitter()
let mainWindow = null
let splashWindow = null

function createWindow () {
  // Create the browser window.
  mainWindow = new BrowserWindow({
    width: 800,
    height: 600,
    show: false,
    webPreferences: {
      preload: path.join(__dirname, 'preload.js'),
      nodeIntegration: false,
      contextIsolation: true
    }
  })

  // Add splash load file
  splashWindow = new BrowserWindow({
    width: 600,
    height: 500,
    frame: false,
    alwaysOnTop: true,
    webPreferences: {
      preload: path.join(__dirname, 'preload.js'),
      nodeIntegration: false,
      contextIsolation: true
    }
  });

  loadingEvents.on('finishedProgressBar', async () =>  {
    try {
      splashWindow.close();
      await mainWindow.loadFile('src/pages/login-google.html');
      mainWindow.center();
      mainWindow.maximize();
      mainWindow.show();
    } catch (error) {
      console.error('Error loading main window:', error);
    }
  })

  loadingEvents.on('progressBar', async data => {
    try {
      splashWindow.webContents.send('progressBar', data);
    } catch (error) {
      console.error('Error sending progressBar event:', error);
    }

    setTimeout(() => loadingEvents.emit('finishedProgressBar'), 1000)
  })

  // Load splash screen file
  splashWindow.loadFile('src/pages/splash.html').then(() => {
    splashWindow.center();
    setTimeout(() => {
      loadingEvents.emit('progressBar', { percentage: 100, step: 2 });
    }, 5000);
  }).catch(error => {
    console.error('Error loading splash window:', error);
  });

  // Clean up ressources when splash window is closed
  splashWindow.on('closed', () => {
    splashWindow.removeAllListeners()
    splashWindow = null
  })

  // Open the DevTools.
  // mainWindow.webContents.openDevTools()
}

// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.whenReady().then(() => {
  createWindow()

  app.on('activate', function () {
    // On macOS it's common to re-create a window in the app when the
    // dock icon is clicked and there are no other windows open.
    if (BrowserWindow.getAllWindows().length === 0) createWindow()
  })

  // if (splashWindow) {
  //   splashWindow.on('closed', () => {
  //     splashWindow.removeAllListeners()
  //     splashWindow = null
  //   })
  // }
})

// Quit when all windows are closed, except on macOS. There, it's common
// for applications and their menu bar to stay active until the user quits
// explicitly with Cmd + Q.
app.on('window-all-closed',  () => {
  if (process.platform !== 'darwin') {
    app.quit()
  }
})

// Error handling for uncaught exceptions
process.on('uncaughtException', (error) => {
  console.error('Uncaught exception:', error);
});

process.on('unhandledRejection', (reason) => {
  console.error('Unhandled rejection:', reason);
});

// In this file you can include the rest of your app's specific main process
// code. You can also put them in separate files and require them here.
Enter fullscreen mode Exit fullscreen mode

This script does the following:

  • Creates a splash window that serves as a loading screen.

  • Emits events (progressBar and finishedProgressBar) to manage the loading steps.

  • Switches to the main window (login-google.html) after loading is complete.


Designing the Splash Screen

The splash screen shows a progress bar and loading animations. Here's the splash.html file:

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <script src="https://cdn.tailwindcss.com"></script>
    <style>
        .bg-primary {
            background-color: rgb(83 105 248 / 1);
        }
    </style>
</head>
<body class="flex items-center justify-center h-screen">
<div aria-label="Loading..." role="status" class="space-x-2">
    <div class="m-auto mb-[5rem] flex justify-center items-center">
        <div class="absolute animate-spin rounded-full h-32 w-32 border-t-4 border-b-4 border-purple-500"></div>
        <img src="https://www.svgrepo.com/show/509001/avatar-thinking-9.svg" class="rounded-full h-28 w-28">
    </div>
    <span class="text-4xl font-medium text-gray-700 flex text-center">Chargement des
        dépendances</span>
    <div class="mt-[2rem]">
        <p class="text-gray-400 mb-3 dark:text-gray-400 text-center" id="step-block">Step 1 / 3</p>
        <div class="w-full h-3 bg-black/10 rounded-full">
            <div id="progress-bar-loading"
                 class="bg-primary h-3 rounded-s-full w-0 animate-strip"
                 style="background-image:linear-gradient(45deg,hsla(0,0%,100%,.15) 25%,transparent 0,transparent 50%,hsla(0,0%,100%,.15) 0,hsla(0,0%,100%,.15) 75%,transparent 0,transparent);background-size:1rem 1rem"></div>
        </div>
    </div>
</div>
<script>
    // Fallback for when IPC messages are not received
    document.addEventListener('DOMContentLoaded', () => {
        const progressBarLoading = document.getElementById('progress-bar-loading');
        const stepBlock = document.getElementById('step-block');

        // Default loading state
        progressBarLoading.style.width = '33%';
        stepBlock.innerHTML = 'Step 1 / 3';
    });

    ipc.on('progressBar', (event, message) => {
        const percentage = message.percentage
        const step = message.step
        // Update DOM element progressBar
        const progressBarLoading = document.getElementById('progress-bar-loading')
        progressBarLoading.classList.remove('rounded-s-full')
        progressBarLoading.classList.add('rounded-full')
        progressBarLoading.style.width = percentage + '%'
        // Update HTML text to step-block
        const stepBlock = document.getElementById('step-block')
        stepBlock.innerHTML = `Step ${step} / 3`
    })
</script>
</body>
</html>
Enter fullscreen mode Exit fullscreen mode

Creating the Login Page

After the splash screen finishes, we transition to a login page (login-google.html). Here's the code:

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <meta name="description" content="Login with Google to access fully functional accounts starting at $19/month.">
    <title>Login with Google</title>
    <link rel="stylesheet" href="../../dist/source/style.css">
</head>
<body>
    <main class="p-6">
        <div class="2xl:px-32 mx-auto">
            <!-- Title -->
            <div class="max-w-lg mx-auto text-center">
                <h2 class="sm:text-2xl text-xl font-semibold mb-2">Login with Google</h2>
                <p class="text-gray-600">Fully functional accounts are starting from $19/month only</p>
            </div>
            <!-- Form -->
            <div class="mt-10 max-w-4xl flex items-center m-auto">
                <div class="card">
                    <div class="p-6">
                        <div class="flex items-center justify-between gap-5">
                            <div>
                                <h6 class="text-base mb-2">Form</h6>
                            </div>
                        </div>
                        <hr class="mb-5">
                        <p class="text-gray-600">
                            It is a long established fact that a reader will be distracted by the readable content of a page when looking at its layout. The point of using Lorem Ipsum is that it has a more-or-less normal distribution of letters, as opposed to using 'Content here, content here', making it look like readable English. Many desktop publishing packages and web page editors now use Lorem Ipsum as their default model text, and a search for 'lorem ipsum' will uncover many web sites still in their infancy. Various versions have evolved over the years, sometimes by accident, sometimes on purpose (injected humour and the like).
                        </p>
                        <div class="mt-14 text-center">
                            <form action="/auth/google" method="POST">
                                <button type="submit" class="btn font-medium px-8 bg-primary/90 text-white hover:bg-primary" aria-busy="false">
                                    <span id="login-text">Login</span>
                                    <span id="loading-spinner" class="hidden animate-spin ml-2"></span>
                                </button>
                            </form>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </main>
    <script>
        const loginButton = document.querySelector('button');
        loginButton.addEventListener('click', () => {
            const spinner = document.getElementById('loading-spinner');
            const text = document.getElementById('login-text');
            spinner.classList.remove('hidden');
            text.textContent = 'Logging in...';
        });
    </script>
</body>
</html>
Enter fullscreen mode Exit fullscreen mode

Final Thoughts

This project demonstrates how to implement a multi-step loading screen in an Electron app. By combining BrowserWindow, EventEmitter, and IPC, we ensure smooth transitions between screens.

electron Article's
30 articles in total
Favicon
First thoughts on Electron
Favicon
Let's build website builder
Favicon
Study With Me 1.0
Favicon
[Boost]
Favicon
Electric Bus Pantograph Market: Trends, Challenges, Drivers, and Insights Through 2033
Favicon
Keyboard Sounds — Make any keyboard sound mechanical
Favicon
Electron
Favicon
I Hate Website Builders – So I Built My Own
Favicon
Is the browser always the right tool for the job?
Favicon
E-House Market Insights: Compact Solutions for Modern Power Challenges
Favicon
.NET Cross-Platform Web Desktop App Frameworks as Electron Alternatives
Favicon
How to remotely EV code-sign a windows application using ssl.com
Favicon
Configuring webpack to handle multiple browser windows in Electron
Favicon
Using native modules in Electron
Favicon
Requesting camera and microphone permission in an Electron app
Favicon
🚀Building a Multi-Step Loading Screen with Electron
Favicon
Building deep-links in Electron application
Favicon
MaweJS: Editor for plantsers
Favicon
Handling TypeORM migrations in Electron apps
Favicon
Unicode-Search - my first Electron app!
Favicon
Creating a synchronized store between main and renderer process in Electron
Favicon
The ultimate Electron app with Next.js and React Server Components
Favicon
Electric Bikes And Coding
Favicon
Creating a Browser Window in Electron: A Step-by-Step Guide
Favicon
How to Create a Windows Executable with Electron Forge that Adds a Desktop Shortcut?
Favicon
Building and publishing an Electron application using electron-builder
Favicon
Cross-compile a distributed Electron App
Favicon
The Only Electron Framework You'll Ever Need: Introducing the Ideal Electron Framework
Favicon
Overcoming Electron-Builder Limitations: A C# and NSIS Hybrid Approach
Favicon
How to Use Electron.js to Create Cross-Platform Desktop Applications

Featured ones: