DEV Community

Cover image for Web Page Height Acquisition, Action Menu Bottom Layout, Animation Issues, Web Cross-Origin Image Loading
kouwei qing
kouwei qing

Posted on

Web Page Height Acquisition, Action Menu Bottom Layout, Animation Issues, Web Cross-Origin Image Loading

[Daily HarmonyOS Next Knowledge] Web Page Height Acquisition, Action Menu Bottom Layout, Animation Issues, Web Cross-Origin Image Loading, System Dark Mode Immunity

1. HarmonyOS: Discrepancy between getPageHeight() and actual web page height in Web control?

Calling getWebViewController().getPageHeight() in the Web's onPageEnd() method returns a height smaller than the actual web content height. Using setTimeout(() => { let webHeight1 = this.controller.getWebViewController().getPageHeight(); Logger.getInstance().error('Internal control height time', ' webHeight:' + webHeight1 + ' webHeight:' + vp2px(webHeight1)); }, 200) obtains the correct height. Is there a way to get the actual height without delay?

onPageEnd is a callback when page loading completes, not when page rendering finishes, hence the height discrepancy. Use onFirstMeaningfulPaint:

Documentation: https://developer.huawei.com/consumer/cn/doc/harmonyos-references-V5/ts-basic-components-web-V5#ZH-CN_TOPIC_0000001847049744__onfirstmeaningfulpaint12

2. Does HarmonyOS promptAction.showActionMenu support bottom layout?

Does the promptAction.showActionMenu interface support pop-up layout at the bottom? The documentation does not mention alignment settings, and examples show centered layouts.

Reference demo:

import promptAction from '@ohos.promptAction';
import { BusinessError } from '@ohos.base';

function showActionMenu() {
  try {
    promptAction.showActionMenu({
      title: 'Title Info',
      buttons: [
        {
          text: 'item1',
          color: '#666666'
        },
        {
          text: 'item2',
          color: '#000000'
        },
      ]
    }, (err, data) => {
      if (err) {
        console.info('showActionMenu err: ' + err);
        return;
      }
      console.info('showActionMenu success callback, click button: ' + data.index);
    })
  } catch (error) {
    let message = (error as BusinessError).message;
    let code = (error as BusinessError).code;
    console.error(`showActionMenu args error code is ${code}, message is ${message}`);
  }
}

@Entry
@Component
struct Index {
  @State message: string = 'Hello World';

  build() {
    RelativeContainer() {
      Text(this.message)
        .id('HelloWorld')
        .fontSize(50)
        .fontWeight(FontWeight.Bold)
        .alignRules({
          center: { anchor: '__container__', align: VerticalAlign.Center },
          middle: { anchor: '__container__', align: HorizontalAlign.Center }
        })
        .onClick(() => {
          showActionMenu();
        })
    }
    .height('100%')
    .width('100%')
  }
}
Enter fullscreen mode Exit fullscreen mode

3. Must HarmonyOS animations be paired with @State-decorated variables to take effect?

Generally, animations require @State-decorated variables. The following demo works with Text().width(300).height(300).backgroundColor(Color.Black):

@Entry
@Component
export default struct ParticleStar {
  aboutToAppear(): void {
  }

  build() {
    Stack() {
      Text()
        .width(300).height(300).backgroundColor(Color.Black);
      Particle({
        particles: [
          {
            emitter: {
              particle: {
                type: ParticleType.POINT, // Particle type
                config: {
                  radius: 10 // Dot radius
                },
                count: 500, // Total particles
                lifetime: 10000 // Particle lifetime in ms
              },
              emitRate: 10, // Particles emitted per second
              position: [200, 0],
              shape: ParticleEmitterShape.RECTANGLE // Emitter shape
            },
            color: {
              range: [Color.Red, Color.Yellow], // Initial color range
              updater: {
                type: ParticleUpdater.CURVE, // Change mode: curve
                config: [
                  {
                    from: Color.White, // Start value
                    to: Color.Pink, // End value
                    startMillis: 0, // Start time
                    endMillis: 3000, // End time
                    curve: Curve.EaseIn // Change curve
                  },
                  {
                    from: Color.Pink,
                    to: Color.Orange,
                    startMillis: 3000,
                    endMillis: 5000,
                    curve: Curve.EaseIn
                  },
                  {
                    from: Color.Orange,
                    to: Color.Pink,
                    startMillis: 5000,
                    endMillis: 8000,
                    curve: Curve.EaseIn
                  },
                ]
              }
            },
            opacity: {
              range: [0.0, 1.0], // Initial opacity range [0.0 to 1.0]
              updater: {
                type: ParticleUpdater.CURVE, // Opacity change mode: curve
                config: [
                  {
                    from: 0.0,
                    to: 1.0,
                    startMillis: 0,
                    endMillis: 3000,
                    curve: Curve.EaseIn
                  },
                  {
                    from: 1.0,
                    to: 0.0,
                    startMillis: 5000,
                    endMillis: 10000,
                    curve: Curve.EaseIn
                  }
                ]
              }
            },
            scale: {
              range: [0.0, 0.0],
              updater: {
                type: ParticleUpdater.CURVE,
                config: [
                  {
                    from: 0.0,
                    to: 0.5,
                    startMillis: 0,
                    endMillis: 3000,
                    curve: Curve.EaseIn
                  }
                ]
              }
            },
            acceleration: {
              // Acceleration configuration: magnitude and direction
              speed: {
                range: [3, 9],
                updater: {
                  type: ParticleUpdater.RANDOM,
                  config: [1, 20]
                }
              },
              angle: {
                range: [90, 90]
              }
            }
          }
        ]
      }).width(300).height(300);
    }.width("100%").height("100%").align(Alignment.Center);
  }
}
Enter fullscreen mode Exit fullscreen mode

4. HarmonyOS Web component cannot load cross-origin image resources?

When an app accesses a web page, some images fail to load due to cross-origin restrictions. How to resolve this?

For cross-origin issues, refer to: https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/web-cross-origin-V5

To enhance security, the ArkWeb kernel prohibits cross-origin requests from file or resource protocols. When loading local offline resources via the Web component, cross-origin access to file/resource protocols is blocked. Two solutions:

Method 1

Use http/https protocols instead of file/resource protocols. Intercept and replace local resources using the Web component's onInterceptRequest method to avoid CORS blocking.

Method 2

Set an allowed path list via setPathAllowingUniversalAccess to enable cross-origin access to local files via the file protocol. Path formats must comply with:

  1. Application file directory (via Context.filesDir):
    • /data/storage/el2/base/files/example
    • /data/storage/el2/base/haps/entry/files/example
  2. Application resource directory (via Context.resourceDir):
    • /data/storage/el1/bundle/entry/resource/resfile
    • /data/storage/el1/bundle/entry/resource/resfile/example
// main/ets/pages/Index.ets
import { webview } from '@kit.ArkWeb';
import { BusinessError } from '@kit.BasicServicesKit';

@Entry
@Component
struct WebComponent {
  controller: WebviewController = new webview.WebviewController();

  build() {
    Row() {
      Web({ src: "", controller: this.controller })
        .onControllerAttached(() => {
          try {
            // Set allowed cross-origin paths
            this.controller.setPathAllowingUniversalAccess([
              getContext().resourceDir,
              getContext().filesDir + "/example"
            ]);
            this.controller.loadUrl("file://" + getContext().resourceDir + "/index.html");
          } catch (error) {
            console.error(`ErrorCode: ${(error as BusinessError).code},  Message: ${(error as BusinessError).message}`);
          }
        })
        .javaScriptAccess(true)
        .fileAccess(true)
        .domStorageAccess(true);
    }
  }
}
Enter fullscreen mode Exit fullscreen mode
<!-- main/resource/rawfile/index.html -->
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="utf-8">
    <title>Demo</title>
    <meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no, viewport-fit=cover">
    <script>
        function getFile() {
            var file = "file:///data/storage/el1/bundle/entry/resources/resfile/js/script.js";
            var xmlHttpReq = new XMLHttpRequest();
            xmlHttpReq.onreadystatechange = function(){
                console.log("readyState:" + xmlHttpReq.readyState);
                console.log("status:" + xmlHttpReq.status);
                if(xmlHttpReq.readyState == 4){
                    if (xmlHttpReq.status == 200) {
                        const element = document.getElementById('text');
                        element.textContent = "load " + file + " success";
                    } else {
                        const element = document.getElementById('text');
                        element.textContent = "load " + file + " failed";
                    }
                }
            }
            xmlHttpReq.open("GET", file);
            xmlHttpReq.send(null);
        }
    </script>
</head>

<body>
<div class="page">
    <button id="example" onclick="getFile()">stealFile</button>
</div>
<div id="text"></div>
</body>

</html>
Enter fullscreen mode Exit fullscreen mode
// main/resources/rawfile/js/script.js
const body = document.body;
const element = document.createElement('div');
element.textContent = 'success';
body.appendChild(element);
Enter fullscreen mode Exit fullscreen mode

5. HarmonyOS: How to prevent adaptation to system dark mode?

To manually set light/dark mode, refer to the official document: https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/arkts-light-dark-color-adaptation-V5

Top comments (0)