Frame in <div>

hello everyone I hope you fine.
trying making mobile app using vanilla and capacitor js, i have made SPA app manually using simple library for reasons.
now i will using ZIM in the project.

the hole idea of my code is to emptying the root element innerHtml and fill it again with another content , when I am doing that i failed
cause:
1- i can see the content of one stage when click on it when trying another button to display the new content i see nothing,

2- also the outercolor of stage still visible on emulator when i navigate to another link "quiz' for example

here is my code

component/ZIM.js


import { Frame, Tag } from "zimjs";
import { Zim1 } from "./allZims/zim1";
import { Zim2 } from "./allZims/zim2";

// مصفوفة تحتوي على بيانات الأزرار
const zimItems = [
  { id: "btn-1", label: "The current button", content: Zim1 },
  { id: "btn-2", label: "A second button item", content: Zim2 },
];


export const showZim = () => {
  
  const app = document.getElementById("app");
  app.innerHTML = ""; // تفريغ المحتوى القديم


  const listGroup = document.createElement("div");
  listGroup.className = "list-group list-group-flush";

  zimItems.forEach((item) => {
    const button = document.createElement("button");
    button.id = item.id;
    button.className = "list-group-item list-group-item-action";
    button.textContent = item.label;

    button.addEventListener("click", () => {

      app.innerHTML = ""; // تفريغ المحتوى السابق
      app.style.backgroundColor = '';
      
      const holderDiv = document.createElement("div");
      holderDiv.id = "holder"; // إنشاء div مخصص للإطار
      holderDiv.style.width = "255";
      holderDiv.style.width = "100%";
      holderDiv.style.height = "488"; // يمكن تعديل الارتفاع
     
      app.appendChild(holderDiv); 

        // التحقق من وجود الـ canvas السابق وإزالته
        const existingCanvas = document.getElementById("holderCanvas");
        if (existingCanvas) {
          existingCanvas.remove();
        }
      
      // إنشاء إطار ZIM داخل الحاوية
      const frame = new Frame({
        scaling: "holder",        // استخدام الوضع holder لربط الإطار بـ div
        width: 255,              // تحديد عرض الإطار
        height: 488,              // تحديد ارتفاع الإطار
        color: light,
        outerColor:dark,
        //tag: "holder", 
        ready: function() {
          const stage = frame.stage;
          item.content(stage);  // استدعاء دالة المحتوى الخاصة بكل زر
          stage.update();
        }
      });
    });

    listGroup.appendChild(button);
  });

  app.appendChild(listGroup);
};


main.js

import { App } from '@capacitor/app';
import { Dialog } from '@capacitor/dialog';
import Navigo from "navigo";
import { learn } from '../components/learn';
import { showQuizzes } from '../components/quiz';
import { showZim } from '../components/zim';


export const router = new Navigo("/", { hash: false, root: "/" }); 

async function handleRoute(callback) {
  const isSessionActive = await checkSession();
  toggleBottomBar(isSessionActive);
  if (isSessionActive) {
    await callback();  
  } else {
    router.navigate('/login');
  }
}

router
  .on('/', async () => {
    const isSessionActive = await checkSession();
    toggleBottomBar(isSessionActive);
    if (isSessionActive) {
      router.navigate('/profile');
    } else {
      signUp();
    }
  })
  .on('/login', async () => {
    const isSessionActive = await checkSession();
    toggleBottomBar(isSessionActive);
    if (!isSessionActive) {
      logIn();
    } else {
      router.navigate('/profile');
    }
  })
  .on('/profile', () => handleRoute(profile))
  .on('/resources', () => handleRoute(Files))
  .on('/lessons', () => handleRoute(Lesson))
  .on('/learn', () => {
    learn();
  })
  .on('/quiz', () => handleRoute(showQuizzes))
  .on('/zim', () => showZim())

  .resolve();

allZim/ZIM1.js

import  { Tile, Scrambler, Frame, Bitmap, Pic, Button, Circle } from "zimjs";

export const Zim1 = function (stage){
    zog("Zim1");
    new Circle(30,blue).pos(100,100).drag()
    new Circle(30,blue).pos(100,120).drag()
    new Circle(30,blue).pos(100,140).drag()
};

allZim/ZIM2.js

import { Tile, Scrambler, Poly, Frame, Label, Rectangle, Emitter, Circle } from "zimjs";

export const Zim2 = function (stage){ 
  zog("Zim2");
  new Circle(30,red).center().drag()
}

i will appreciate any helping

1 Like

Well.... if I were emptying everything, I would probably just go to another page but maybe you have your reasons. There is F.dispose() to get rid of a Frame - are you going to go back to the same Frame later?

The outerColor is just some css on maybe the body or the holder tag, I can't remember so you should be able to adjust that once you find out. You can set the outerColor to clear to make it transparent.

Not sure if any of that helped, perhaps others who swap frames, etc. can help out.

yes, dispose() helped , and i fixed the outercolor.
you mean by "swap frames" is making many page() obj in same frame?
what i am donig is a list of 10-11-9 educational activities where user can choose and learn,play.

i adjust my code in this way and set the width,height in css according to emulator size which is not good, but i surprised how stage look !!
not even close to FIT mode.



import { Frame, Waiter } from "zimjs";
import { Zim1 } from "./allZims/zim1";
import { Zim2 } from "./allZims/zim2";

// مصفوفة تحتوي على بيانات الأزرار
const zimItems = [
  { id: "btn-1", label: "The current button", content: Zim1 },
  { id: "btn-2", label: "A second button item", content: Zim2 },
];

let currentFrame = null; // لتخزين الإطار الحالي

export const showZim = () => {
  const app = document.getElementById("app");
  app.innerHTML = ""; // تفريغ المحتوى القديم
  //app.style.backgroundColor = 'white';

  const listGroup = document.createElement("div");
  listGroup.className = "list-group list-group-flush";

  zimItems.forEach((item) => {
    const button = document.createElement("button");
    button.id = item.id;
    button.className = "list-group-item list-group-item-action";
    button.textContent = item.label;

    button.addEventListener("click", () => {
      // تنظيف الإطار السابق
      if (currentFrame) {
        currentFrame.dispose();  // إزالة الإطار الحالي إذا كان موجودًا
        currentFrame = null;
      }

      // إزالة أي عناصر زائدة مثل الـ <style>
      const existingCanvas = document.getElementById("holderCanvas");
      if (existingCanvas) {
        existingCanvas.remove();
      }

      app.innerHTML = ""; // تفريغ المحتوى السابق
      app.style.backgroundColor = "";

      // إنشاء الحاوية الجديدة للإطار
      const holderDiv = document.createElement("div");
      holderDiv.id = "holder";
      holderDiv.style.width = "100%";
      holderDiv.style.height = "480px";
      app.appendChild(holderDiv);

      // إنشاء إطار ZIM جديد
      const frame = new Frame({
        scaling: "holder",
        width: 1024,
        height: 768,
        progress: new Waiter({
          backgroundColor: blue,
          corner: 10,
        }),
        allowDefault: true,
        assets: ['scramble.png'],
        path: "/assets/",
        color: light,
        outerColor: clear,
        ready: function () {
          const stage = frame.stage;
          item.content(stage); // استدعاء دالة المحتوى الخاصة بالزر
          stage.update();
        },
      });

      currentFrame = frame;  // تخزين الإطار الجديد لإزالته لاحقًا
    });

    listGroup.appendChild(button);
  });

  app.appendChild(listGroup);
};

you can see, here is a button testing and pic on stage

You can also use FULL mode and then do scaling yourself - or with the Layout class.

to save the time i was hoping to be able to use FIT mode in the div, and run out of handling that manually , is there a way?

It sounds to me like you can use one Frame in FIT mode and put each app in its own ZIM Container (or ZIM Page). The addTo() and removeFrom() the pages as desired. This is very common. You do not need to load all assets (images and sounds) all at the start, you can use F.loadAssets() at any time. Assign this to a variable and use variable.on("complete", ()=>{}) to get those assets when ready and display the appropriate app. We would be happy to help if you run into questions.

Another way to do it is just put each app in its own HTML page and go between them.

Do you have any concerns about why one of these two ways will not work?

well, i will try the first way.
backing to div problem : i changed my code a little

 holderDiv.style.width = "265px";
 holderDiv.style.height = "488px";

const frame = new Frame({
        scaling: FIT,
        width: 1024,
        height: 768,
        progress: new Waiter({
          backgroundColor: blue,
          corner: 10,
        }),

and it's give the expected result, but you can see the div style is fixed according to 1 device screen

By the first way, do you mean just the regular FIT template? Or the first way that you tried at the start of the overall question?

If you are using FIT, why are you setting div sizes? Just use the template at the top of the page here: https://zimjs.com/code.html