npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

@hadss/react_native_geometry_transition

v1.0.8

Published

Shared Element Transition with OpenHarmony feature

Readme

@hadss/react_native_geometry_transition

介绍

为基于 React Native 的混合开发工程提供一镜到底能力。

工程目录

.
├── harmony
│   ├── geometry_transition
│   │   ├── src
│   │   │   └── main
│   │   │       ├── cpp     // CAPI一镜到底组件实现
│   │   │       │   ├── CMakeLists.txt
│   │   │       │   ├── GeometryViewComponentDescriptor.h
│   │   │       │   ├── GeometryViewComponentInstance.cpp
│   │   │       │   ├── GeometryViewComponentInstance.h
│   │   │       │   ├── GeometryViewEventEmitter.cpp
│   │   │       │   ├── GeometryViewEventEmitter.h
│   │   │       │   ├── GeometryViewJSIBinder.h
│   │   │       │   ├── GeometryViewNode.cpp
│   │   │       │   ├── GeometryViewNode.h
│   │   │       │   ├── GeometryViewPackage.cpp
│   │   │       │   ├── GeometryViewPackage.h
│   │   │       │   ├── Index.d.ts
│   │   │       │   ├── Props.cpp
│   │   │       │   ├── Props.h
│   │   │       │   └── oh-package.json5
│   │   │       ├── ets    // ArkTS一镜到底组件实现
│   │   │       │   ├── GeometryTransitionView.ets
│   │   │       │   ├── GeometryTransitionViewPackages.ts
│   │   │       │   └── generated
│   │   │       │       ├── components
│   │   │       │       │   ├── GeometryTransitionView.ts
│   │   │       │       │   └── ts.ts
│   │   │       │       ├── index.ets
│   │   │       │       ├── ts.ts
│   │   │       │       └── turboModules
│   │   │       │           └── ts.ts
│   │   │       └── module.json5
│   │   └── ts.ts
│   └── geometry_transition.har
├── package.json
├── src
│   ├── fabric
│   │   ├── GeometryView.tsx   // CAPI一镜到底组件
│   │   └── v1
│   │       └── GeometryTransitionViewNativeComponent.tsx   // ArkTS一镜到底组件
│   └── index.tsx

安装与使用

进入到工程目录并输入以下命令:

npm

npm install @hadss/react_native_geometry_transition

yarn

yarn add @hadss/react_native_geometry_transition

下面的代码展示了这个库的基本使用场景: 前置条件:ArkUI的navigation控制路由跳转,NavDestination加载对应的RN bundle。

场景一:当前页面是RN页面,目标页面是原生页面

PlayList页面

使用了GeometryView组件,并设置了geometryViewID为'test',点击事件调用原生navPathStack.pushPath,跳转MusicPlay页面。

function PlayList() {
  return (
    <SafeAreaView style={styles.container}>
      <GeometryView
        style={[styles.geometryView, { top: 100, left: 100 }]} 
        geometryViewID={'test'}
        onGeometryViewClick={() => {
          SampleTurboModule.pushStringToHarmony('pages/MusicPlay', 1);
        }}
      />
    </SafeAreaView>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
    justifyContent: 'center',
    alignItems: 'center',
    width: '100%',
    height: '100%',
  },
  geometryView: {
    position: 'absolute', 
    width: 60,
    height: 60,
    backgroundColor: 'red',
  },
});

export default PlayList;

MusicPlay页面

function MusicPlay() {
  return (
    <SafeAreaView style={styles.container}>
      <GeometryView style={{ width: 200, height: 200, backgroundColor: 'red' }} geometryViewID={'test'} onGeometryViewClick={() => {
        SampleTurboModule.pushStringToHarmony('pop', 2)
      }} >
      </GeometryView>
    </SafeAreaView>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
    justifyContent: 'center',
    alignItems: 'center',
    width: '100%',
    height: '100%'
  },
  text: {
    fontSize: 18,
    color: '#333',
  },
});

export default MusicPlay;

原生侧

PlayListPage监听RN页面跳转事件,如果事件触发,调用this.navPathStack.pushPath({ name: 'MusicPlayPage' }),跳转至原生MusicPlayPage页面。

@Component
export default struct PlayListPage {
  private instance: RNInstance = LoadManager.instance;
  private bundlePath = 'bundle/playlist.harmony.bundle'
  private moduleName = 'PlayList'
  @StorageLink('isMetroAvailable') isMetroAvailable: boolean = false
  @Consume('navPathStack') navPathStack: NavPathStack

  aboutToAppear() {
    emitter.on({ eventId: 1 }, () => {
      animateTo({duration: 700, curve: Curve.Friction}, () => {
        this.navPathStack.pushPath({ name: 'MusicPlayPage' })
      });
    });
  }

  aboutToDisappear() {
    emitter.off(1);
  }

  build() {
    NavDestination() {
      if (this.isMetroAvailable) {
        MetroBaseRN({
          moduleName: this.moduleName,
        }).align(Alignment.Top).margin({ top: 0 })
      } else if (this.instance) {
        BaseRN({
          rnInstance: this.instance,
          moduleName: this.moduleName,
          bundlePath: this.bundlePath,
        }).align(Alignment.Top).margin({ top: 0 })
      } else {
        Text('加载失败')
      }
    }
    .hideTitleBar(true)
  }
}

MusicPlayPage加载RN侧MusicPlay页面

@Component
export default struct MusicPlayPage {
  private instance: RNInstance = LoadManager.instance
  private bundlePath = 'bundle/musicplay.harmony.bundle'
  private moduleName = 'MusicPlay'
  @StorageLink('isMetroAvailable') isMetroAvailable: boolean = false
  @Consume('navPathStack') navPathStack: NavPathStack

  aboutToAppear() {
    emitter.on({ eventId: 2 }, () => {
      animateTo({duration: 700, curve: Curve.Friction}, () => {
        this.navPathStack.pop()
      });
    });
  }

  aboutToDisappear() {
    emitter.off(2);
  }

  build() {
    NavDestination() {
      if (this.isMetroAvailable) {
        MetroBaseRN({
          moduleName: this.moduleName,
        })
          .align(Alignment.Top).margin({ top: 20 })
      } else if (this.instance) {
        BaseRN({
          rnInstance: this.instance,
          moduleName: this.moduleName,
          bundlePath: this.bundlePath,
        }).align(Alignment.Top).margin({ top: 20 })
      } else {
        Text('加载失败')
      }
    }
    .geometryTransition('test')   // 设置同RN侧一致的id
    .hideTitleBar(true)
  }
}

场景二:当前页面和目标页面均是RN页面

RN侧当前页面

使用了GeometryTransitionView组件,并设置了geometryViewID为'test',pageType设置为'current',点击跳转按钮调用原生navPathStack.pushPath,跳转至目标页面。

Tip: duration动画执行时间在当前页面设置不会生效,具体动画执行时间由目标页面的duration控制。

    <View style={{ width: '100%', height: '100%', justifyContent: 'center', alignItems: 'center' }}>
      <GeometryTransitionView geometryViewID='test' style={{ width: 100, height: 100, backgroundColor: 'black' }} isFollow={false} duration={700} pageType={'current'}>
      </GeometryTransitionView>
      <Button title="跳转" onPress={() => { SampleTurboModule.pushStringToHarmony('pages/TargetPage', 1); }}></Button>
    </View>

原生侧当前页面

监听路由跳转事件,跳转至目标页面。

  aboutToAppear() {
    emitter.on({ eventId: 1 }, (eventData) => {
      this.pagePathsGoods.pushPath({ name: "TargetPage" }, false);
    });
  }

RN侧目标页面

使用了GeometryTransitionView组件,并设置了与 “当前页面“ 一致的geometryViewID:'test',pageType设置为'target',duration为设置从 “当前页面“ 跳转至目标页面的动画执行时间,点击返回按钮调用原生navPathStack.pop,返回 “当前页面“ 。

    <View style={{ width: '100%', height: '50%', justifyContent: 'flex-end', alignItems: 'center', paddingBottom: '120' }}>
      <GeometryTransitionView
        style={{ width: 200, height: 200, backgroundColor: 'red' }}
        geometryViewID={'test'}
        isFollow={false}
        duration={700}
        pageType={'target'}
      >
      </GeometryTransitionView>
      <Button title="返回" onPress={() => { SampleTurboModule.pushStringToHarmony('pages/CurrentPage', 2); }}></Button>
    </View>

原生侧目标页面

监听路由返回事件,使用动画返回跳转至 “当前页面“ 。

  aboutToAppear() {
    emitter.on({ eventId: 2 }, (eventData) => {
      if (eventData.data) {
        const path: string = eventData.data['param'];
        if (path === "pages/CurrentPage") {
          animateTo({ duration: 700 }, () => {
            this.pagePathsGoods.pop(false);
          })
        }
      }
    });
  }

Link

目前OpenHarmony暂不支持AutoLink,所以Link步骤需要手动配置。

首先需要使用DevEco Studio打开项目里的OpenHarmony工程,在工程根目录的 oh-package.json5 添加 overrides 字段:

{
  ...
  "overrides": {
    "@rnoh/react-native-openharmony" : "./react_native_openharmony"
  }
}

引入原生端代码

目前有两种方法:

方法一:

  1. 通过har包引入(在IDE完善相关功能后该方法会被遗弃,目前首选此方法)

    说明: har包位于三方库安装路径的harmony文件夹下。

    a.打开entry/oh-package.json5,添加以下依赖:

    "dependencies": {
        "@rnoh/react-native-openharmony": "file:../react_native_openharmony",
        "@hadss/react_native_geometry_transition": "file:../../node_modules/@hadss/react_native_geometry_transition/harmony/geometry_transition.har",
      }

    b.配置CMakeLists和引入GeometryViewPackage:

    打开entry/src/main/cpp/CMakeLists.txt,添加:

    project(rnapp)
    cmake_minimum_required(VERSION 3.4.1)
    set(CMAKE_SKIP_BUILD_RPATH TRUE)
    set(OH_MODULE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../../oh_modules")
    set(RNOH_APP_DIR "${CMAKE_CURRENT_SOURCE_DIR}")
       
    set(RNOH_CPP_DIR "${OH_MODULE_DIR}/@rnoh/react-native-openharmony/src/main/cpp")
    set(RNOH_GENERATED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/generated")
    set(CMAKE_ASM_FLAGS "-Wno-error=unused-command-line-argument -Qunused-arguments")
    set(CMAKE_CXX_FLAGS "-fstack-protector-strong -Wl,-z,relro,-z,now,-z,noexecstack -s -fPIE -pie")
    add_compile_definitions(WITH_HITRACE_SYSTRACE)
    set(WITH_HITRACE_SYSTRACE 1) # for other CMakeLists.txt files to use
    + add_subdirectory("${OH_MODULE_DIR}/@hadss/react_native_geometry_transition/src/main/cpp" ./geometry_transition)
       
    add_subdirectory("${RNOH_CPP_DIR}" ./rn)
       
    add_library(rnoh_app SHARED
        "./PackageProvider.cpp"
        "${RNOH_CPP_DIR}/RNOHAppNapiBridge.cpp"
    )
       
    target_link_libraries(rnoh_app PUBLIC rnoh)
    + target_link_libraries(rnoh_app PUBLIC geometry_transition)

    c.打开entry/src/main/cpp/PackageProvider.cpp,添加:

    #include "RNOH/PackageProvider.h"
    + #include "GeometryViewPackage.h"
       
    using namespace rnoh;
       
    std::vector<std::shared_ptr<Package>> PackageProvider::getPackages(Package::Context ctx) {
        return {
    +        std::make_shared<GeometryViewPackage>(ctx)
        };
    }

    d.运行:

    点击右上角的sync按钮

    或者在终端执行:

    cd entry
    ohpm install

    然后编译、运行即可。

  2. 在 ArkTS 侧引入 GeometryTransitionView

      ...
    +  import { GeometryTransitionView } from '@hadss/react_native_geometry_transition';
       
     @Builder
     export function buildCustomRNComponent(ctx: ComponentBuilderContext) {
      ...
    + if (ctx.componentName === GeometryTransitionView.NAME) {
    +   GeometryTransitionView({
    +     ctx: ctx.rnComponentContext,
    +     tag: ctx.tag
    +   })
    + }
    ...
    }
    ...

    entry/src/main/ets/pages/index.etsentry/src/main/ets/rn/LoadBundle.ets 找到常量 arkTsComponentNames 在其数组里添加组件名

    const arkTsComponentNames: Array<string> = [
      SampleView.NAME,
      GeneratedSampleView.NAME,
      PropsDisplayer.NAME,
    + GeometryTransitionView.NAME
      ];
  3. 在 ArkTS 侧引入 GeometryTransitionViewPackage

      ...
    + import { GeometryTransitionViewPackage } from '@hadss/react_native_geometry_transition/ts';
       
    export function createRNPackages(ctx: RNPackageContext): RNPackage[] {
      return [
        new SamplePackage(ctx),
    +   new GeometryTransitionViewPackage(ctx)
      ];
    }

方法二:

直接链接源码

如需使用直接链接源码,请参考直接链接源码说明

API

说明: "Platform"列表示支持的平台,All表示支持所有平台。

GeometryView

| Name | Description | Type | Platform | | ------------------- | --------------------- | -------- | ----------- | | geometryViewID | 共享元素ID. | string | OpenHarmony | | onGeometryViewClick | 点击GeometryView回调. | callback | OpenHarmony |

GeometryTransitionView

| Name | Description | Type | Platform | | -------------- | ------------------------------------------------------------ | --------------------- | ----------- | | geometryViewID | 用于设置绑定关系,id置空字符串清除绑定关系避免参与共享行为,id可更换重新建立绑定关系。同一个id只能有两个组件绑定且是in/out不同类型角色,不能多个组件绑定同一个id。 | string | OpenHarmony | | isFollow | 组件内共享元素转场动画参数。true代表跟随做共享动画,false代表不跟随做共享动画,默认值:false。 | boolean? | OpenHarmony | | duration | 动画执行时间,默认值:1000ms。 | number? | OpenHarmony | | pageType | 页面标志。 | 'current' | 'target' | OpenHarmony |

约束与限制:

1、仅限用于原生 Navigation 路由跳转场景;

2、在使用GeometryTransitionView时,如果跳转页面前需要重置geometryViewID,需使用以下方式:

UIManager.dispatchViewManagerCommand(
  findNodeHandle(nativeRef.current),
  'changeGeometryTransitionID',
  ['geometryTransitionID'],   // 共享元素ID
);

并且通过监听geometryTransitionIDChanged事件进行页面跳转,避免target页面先触发build导致一镜到底动效失效问题。

DeviceEventEmitter.addListener('geometryTransitionIDChanged', () => {
  console.log('geometryTransitionIDChanged');
});