HBuilderX

HBuilderX

极客开发工具
uni-app

uni-app

开发一次,多端覆盖
uniCloud

uniCloud

云开发平台
HTML5+

HTML5+

增强HTML5的功能体验
MUI

MUI

上万Star的前端框架

无阻碍点击事件

是否可以在这个基础上我定制个无阻碍点击事件,如1. 用户在微信中点击“按住说话”按钮。

  1. AccessibilityService 检测到点击事件。
  2. AccessibilityService 通过 WebView.evaluateJavascript 调用 JavaScript 函数 onWeChatButtonClicked
  3. JavaScript 函数执行相应的逻辑(如弹出提示)
继续阅读 »

是否可以在这个基础上我定制个无阻碍点击事件,如1. 用户在微信中点击“按住说话”按钮。

  1. AccessibilityService 检测到点击事件。
  2. AccessibilityService 通过 WebView.evaluateJavascript 调用 JavaScript 函数 onWeChatButtonClicked
  3. JavaScript 函数执行相应的逻辑(如弹出提示)
收起阅读 »

Flutter3.27实战2025抖音app直播商城

flutter

原创自研flutter3.27+dart3.6实战抖音短视频+聊天+直播电商带货app商城应用程序。

Flutter3.27仿抖音短视频+直播+聊天app商城系统

img

img

技术栈

  • 编辑器:vscode
  • 技术框架:flutter3.27.1+Dart3.6.0
  • 路由/状态管理:get: ^4.6.6
  • 本地缓存服务:get_storage: ^2.1.1
  • 瀑布流组件:flutter_staggered_grid_view^0.7.0
  • 轮播图组件:card_swiper^3.0.1
  • toast弹窗组件:shirne_dialog^4.8.3
  • 视频套件:media_kit: ^1.1.11

img

img

实现类似抖音app首页左右滑动切换页面内容,上下滑动切换短视频效果。

img

项目框架

img

目前flutter3-douyin-mall短视频直播商城项目已经同步到我的原创作品集。
Flutter3.27仿抖音短视频+直播+聊天app商城系统

img

img

img

flutter3实现首页轮播图+tab滚动吸附

img

return Scaffold(  
  backgroundColor: Colors.grey[50],  
  body: ScrollConfiguration(  
    behavior: CustomScrollBehavior().copyWith(scrollbars: false),  
    child: CustomScrollView(  
      scrollBehavior: CustomScrollBehavior().copyWith(scrollbars: false),  
      controller: scrollController,  
      slivers: [  
        SliverAppBar(  
          backgroundColor: Colors.transparent,  
          foregroundColor: Colors.white,  
          pinned: true,  
          expandedHeight: 200.0,  
          titleSpacing: 10.0,  
          // 搜索框(高斯模糊背景)  
          title: ClipRRect(  
            borderRadius: BorderRadius.circular(30.0),  
            child: BackdropFilter(  
              filter: ImageFilter.blur(sigmaX: 10.0, sigmaY: 10.0),  
              child: Container(  
                ...  
              ),  
            ),  
          ),  
          actions: [  
            IconButton(icon: Icon(Icons.shopping_cart_outlined), onPressed: () {},),  
          ],  
          // 自定义伸缩区域(轮播图)  
          flexibleSpace: Container(  
            decoration: BoxDecoration(  
              gradient: LinearGradient(  
                begin: Alignment.topLeft,  
                end: Alignment.bottomRight,  
                colors: [  
                  Color(0xFFFF5000), Color(0xFFfcaec4)  
                ]  
              )  
            ),  
            child: FlexibleSpaceBar(  
              background: Swiper.children(  
                pagination: SwiperPagination(  
                  builder: DotSwiperPaginationBuilder(  
                    color: Colors.white70,  
                    activeColor: Colors.white,  
                  )  
                ),  
                indicatorLayout: PageIndicatorLayout.SCALE,  
                children: [  
                  Image.network('https://m.360buyimg.com/babel/jfs/t20271217/224114/35/38178/150060/6760d559Fd654f946/968c156726b6e822.png',),  
                  Image.network('https://m.360buyimg.com/babel/jfs/t20280117/88832/5/48468/139826/6789cbcfF4e0b2a3d/9dc54355b6f65c40.jpg',),  
                  Image.network('https://m.360buyimg.com/babel/jfs/t20280108/255505/29/10540/137372/677ddbc1F6cdbbed0/bc477fadedef22a8.jpg',),  
                ],  
              ),  
            ),  
          ),  
        ),  

        ...  

        // tabbar列表  
        SliverPersistentHeader(  
          pinned: true,  
          delegate: CustomStickyHeader(  
            child: PreferredSize(  
              preferredSize: Size.fromHeight(45.0),  
              child: Container(  
                ...  
              ),  
            ),  
          ),  
        ),  

        // 瀑布流列表  
        ...  
      ],  
    ),  
  ),  
  // 返回顶部  
  floatingActionButton: Backtop(controller: scrollController, offset: scrollOffset),  
);

img

img

img

img

flutter3实现短视频功能

img

img

@override  
Widget build(BuildContext context) {  
  return Scaffold(  
    key: scaffoldKey,  
    extendBodyBehindAppBar: true,  
    appBar: AppBar(  
      forceMaterialTransparency: true,  
      backgroundColor: [0, 1, 4, 5].contains(videoModuleController.videoTabIndex.value) ? null : Colors.transparent,  
      foregroundColor: [0, 1, 4, 5].contains(videoModuleController.videoTabIndex.value) ? Colors.black : Colors.white,  
      titleSpacing: 1.0,  
      leading: Obx(() => IconButton(  
        icon: Badge.count(  
          backgroundColor: Colors.red,  
          count: 6,  
          child: Icon(Icons.sort_rounded, color: tabColor(),),  
        ),  
        onPressed: () {  
          // 自定义打开右侧drawer  
          scaffoldKey.currentState?.openDrawer();  
        },  
      )),  
      title: Obx(() {  
        return ScrollConfiguration(  
          behavior: CustomScrollBehavior().copyWith(scrollbars: false),  
          child: TabBar(  
            ...  
          ),  
        );  
      }),  
      actions: [  
        Obx(() => IconButton(icon: Icon(Icons.search_rounded, color: tabColor(),), onPressed: () {},),),  
      ],  
    ),  
    body: ScrollConfiguration(  
      behavior: CustomScrollBehavior().copyWith(scrollbars: false),  
      child: PageView(  
        controller: pageController,  
        onPageChanged: (index) {  
          videoModuleController.updateVideoTabIndex(index);  
          setState(() {  
            tabController.animateTo(index, duration: Duration(milliseconds: 200), curve: Curves.easeInOut);  
          });  
        },  
        children: [  
          ...tabModules  
        ],  
      ),  
    ),  
    // 侧边栏  
    drawer: Drawer(  
      shape: RoundedRectangleBorder(borderRadius: BorderRadius.horizontal(right: Radius.circular(15.0))),  
      clipBehavior: Clip.antiAlias,  
      width: 300,  
      child: Container(  
        ...  
      ),  
    ),  
  );  
}

img

img

img

img

img

img

img

img

flutter3实现直播功能

img

img

// flutter3直播模块  Q:282310962  

@override  
Widget build(BuildContext context) {  
  return Scaffold(  
    backgroundColor: Colors.black,  
    extendBodyBehindAppBar: true,  
    appBar: AppBar(  
      forceMaterialTransparency: true,  
      backgroundColor: Colors.black,  
      foregroundColor: Colors.white,  
      toolbarHeight: 0,  
    ),  
    body: Column(  
      children: [  
        Expanded(  
          child: Stack(  
            children: [  
              PageView.builder(  
                scrollBehavior: CustomScrollBehavior().copyWith(scrollbars: false),  
                scrollDirection: Axis.vertical,  
                controller: pageVerticalController,  
                onPageChanged: (index) async {  
                  setState(() {  
                    liveIndex = index;  
                  });  
                  player.stop();  
                  await player.open(Media(liveJson[index]['src']));  
                },  
                itemCount: liveJson.length,  
                itemBuilder: (context, index) {  
                  return Stack(  
                    children: [  
                      // 视频区域  
                      Positioned(  
                        ...  
                      ),  

                      /// 水平滚动模块(清屏/浮层)  
                      PageView(  
                        scrollDirection: Axis.horizontal,  
                        controller: pageHorizontalController,  
                        onPageChanged: (index) {  
                          // ...  
                        },  
                        children: [  
                          // 直播清屏  
                          Container(  
                            ...  
                          ),  
                          // 直播浮层  
                          Stack(  
                            children: [  
                              // 顶部区域  
                              Positioned(  
                                top: MediaQuery.of(context).padding.top + 7,  
                                left: 10.0,  
                                right: 0,  
                                child: Column(  
                                  crossAxisAlignment: CrossAxisAlignment.start,  
                                  children: [  
                                    // 直播间头像  
                                    Container(  
                                      ...  
                                    ),  
                                    // 排名统计  
                                    Container(  
                                      ...  
                                    ),  
                                    // 红包活动  
                                    Container(  
                                      ...  
                                    ),  
                                  ],  
                                ),  
                              ),  
                              // 底部区域  
                              Positioned(  
                                child: Column(  
                                  crossAxisAlignment: CrossAxisAlignment.start,  
                                  children: [  
                                    // 商品购买动效  
                                    Container(  
                                      ...  
                                    ),  

                                    // 送礼物动效  
                                    AnimationLiveGift(  
                                      giftQueryList: [  
                                        {'label': '小心心', 'gift': 'assets/images/gift/gift1.png', 'user': 'Jack', 'avatar': 'assets/images/avatar/img02.jpg', 'num': 12},  
                                        {'label': '棒棒糖', 'gift': 'assets/images/gift/gift2.png', 'user': 'Andy', 'avatar': 'assets/images/avatar/img06.jpg', 'num': 36},  
                                        {'label': '大啤酒', 'gift': 'assets/images/gift/gift3.png', 'user': '一条咸鱼', 'avatar': 'assets/images/avatar/img01.jpg', 'num': 162},  
                                        ...  
                                      ],  
                                    ),  

                                    // 加入直播间动效  
                                    AnimationLiveJoin(  
                                      joinQueryList: [  
                                        {'avatar': 'assets/images/logo.png', 'name': 'andy'},  
                                        {'avatar': 'assets/images/logo.png', 'name': 'jack'},  
                                        ...  
                                      ],  
                                    ),  

                                    // 直播弹幕+商品讲解  
                                    Container(  
                                      margin: EdgeInsets.only(top: 7.0),  
                                      height: 200.0,  
                                      child: Row(  
                                        ...  
                                      ),  
                                    ),  

                                    // 底部工具栏  
                                    Container(  
                                      margin: const EdgeInsets.only(top: 7.0),  
                                      child: Row(  
                                        ...  
                                      ),  
                                    ),  
                                  ],  
                                ),  
                              ),  
                            ],  
                          ),  
                        ],  
                      ),  
                    ],  
                  );  
                },  
              ),  
            ],  
          ),  
        ),  
      ],  
    ),  
  );  
}

以上就是flutter3.27.1实战抖音app商城的一些知识分享,整个项目涉及到知识点蛮多。希望以上分享对小伙伴们有些帮助哈~~

往期热文

https://segmentfault.com/a/1190000045042968
https://segmentfault.com/a/1190000045245775
https://segmentfault.com/a/1190000045381943
https://segmentfault.com/a/1190000045556523
https://segmentfault.com/a/1190000045667190

作者:xiaoyan2017
链接: https://segmentfault.com/a/1190000046075489
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

继续阅读 »

原创自研flutter3.27+dart3.6实战抖音短视频+聊天+直播电商带货app商城应用程序。

Flutter3.27仿抖音短视频+直播+聊天app商城系统

img

img

技术栈

  • 编辑器:vscode
  • 技术框架:flutter3.27.1+Dart3.6.0
  • 路由/状态管理:get: ^4.6.6
  • 本地缓存服务:get_storage: ^2.1.1
  • 瀑布流组件:flutter_staggered_grid_view^0.7.0
  • 轮播图组件:card_swiper^3.0.1
  • toast弹窗组件:shirne_dialog^4.8.3
  • 视频套件:media_kit: ^1.1.11

img

img

实现类似抖音app首页左右滑动切换页面内容,上下滑动切换短视频效果。

img

项目框架

img

目前flutter3-douyin-mall短视频直播商城项目已经同步到我的原创作品集。
Flutter3.27仿抖音短视频+直播+聊天app商城系统

img

img

img

flutter3实现首页轮播图+tab滚动吸附

img

return Scaffold(  
  backgroundColor: Colors.grey[50],  
  body: ScrollConfiguration(  
    behavior: CustomScrollBehavior().copyWith(scrollbars: false),  
    child: CustomScrollView(  
      scrollBehavior: CustomScrollBehavior().copyWith(scrollbars: false),  
      controller: scrollController,  
      slivers: [  
        SliverAppBar(  
          backgroundColor: Colors.transparent,  
          foregroundColor: Colors.white,  
          pinned: true,  
          expandedHeight: 200.0,  
          titleSpacing: 10.0,  
          // 搜索框(高斯模糊背景)  
          title: ClipRRect(  
            borderRadius: BorderRadius.circular(30.0),  
            child: BackdropFilter(  
              filter: ImageFilter.blur(sigmaX: 10.0, sigmaY: 10.0),  
              child: Container(  
                ...  
              ),  
            ),  
          ),  
          actions: [  
            IconButton(icon: Icon(Icons.shopping_cart_outlined), onPressed: () {},),  
          ],  
          // 自定义伸缩区域(轮播图)  
          flexibleSpace: Container(  
            decoration: BoxDecoration(  
              gradient: LinearGradient(  
                begin: Alignment.topLeft,  
                end: Alignment.bottomRight,  
                colors: [  
                  Color(0xFFFF5000), Color(0xFFfcaec4)  
                ]  
              )  
            ),  
            child: FlexibleSpaceBar(  
              background: Swiper.children(  
                pagination: SwiperPagination(  
                  builder: DotSwiperPaginationBuilder(  
                    color: Colors.white70,  
                    activeColor: Colors.white,  
                  )  
                ),  
                indicatorLayout: PageIndicatorLayout.SCALE,  
                children: [  
                  Image.network('https://m.360buyimg.com/babel/jfs/t20271217/224114/35/38178/150060/6760d559Fd654f946/968c156726b6e822.png',),  
                  Image.network('https://m.360buyimg.com/babel/jfs/t20280117/88832/5/48468/139826/6789cbcfF4e0b2a3d/9dc54355b6f65c40.jpg',),  
                  Image.network('https://m.360buyimg.com/babel/jfs/t20280108/255505/29/10540/137372/677ddbc1F6cdbbed0/bc477fadedef22a8.jpg',),  
                ],  
              ),  
            ),  
          ),  
        ),  

        ...  

        // tabbar列表  
        SliverPersistentHeader(  
          pinned: true,  
          delegate: CustomStickyHeader(  
            child: PreferredSize(  
              preferredSize: Size.fromHeight(45.0),  
              child: Container(  
                ...  
              ),  
            ),  
          ),  
        ),  

        // 瀑布流列表  
        ...  
      ],  
    ),  
  ),  
  // 返回顶部  
  floatingActionButton: Backtop(controller: scrollController, offset: scrollOffset),  
);

img

img

img

img

flutter3实现短视频功能

img

img

@override  
Widget build(BuildContext context) {  
  return Scaffold(  
    key: scaffoldKey,  
    extendBodyBehindAppBar: true,  
    appBar: AppBar(  
      forceMaterialTransparency: true,  
      backgroundColor: [0, 1, 4, 5].contains(videoModuleController.videoTabIndex.value) ? null : Colors.transparent,  
      foregroundColor: [0, 1, 4, 5].contains(videoModuleController.videoTabIndex.value) ? Colors.black : Colors.white,  
      titleSpacing: 1.0,  
      leading: Obx(() => IconButton(  
        icon: Badge.count(  
          backgroundColor: Colors.red,  
          count: 6,  
          child: Icon(Icons.sort_rounded, color: tabColor(),),  
        ),  
        onPressed: () {  
          // 自定义打开右侧drawer  
          scaffoldKey.currentState?.openDrawer();  
        },  
      )),  
      title: Obx(() {  
        return ScrollConfiguration(  
          behavior: CustomScrollBehavior().copyWith(scrollbars: false),  
          child: TabBar(  
            ...  
          ),  
        );  
      }),  
      actions: [  
        Obx(() => IconButton(icon: Icon(Icons.search_rounded, color: tabColor(),), onPressed: () {},),),  
      ],  
    ),  
    body: ScrollConfiguration(  
      behavior: CustomScrollBehavior().copyWith(scrollbars: false),  
      child: PageView(  
        controller: pageController,  
        onPageChanged: (index) {  
          videoModuleController.updateVideoTabIndex(index);  
          setState(() {  
            tabController.animateTo(index, duration: Duration(milliseconds: 200), curve: Curves.easeInOut);  
          });  
        },  
        children: [  
          ...tabModules  
        ],  
      ),  
    ),  
    // 侧边栏  
    drawer: Drawer(  
      shape: RoundedRectangleBorder(borderRadius: BorderRadius.horizontal(right: Radius.circular(15.0))),  
      clipBehavior: Clip.antiAlias,  
      width: 300,  
      child: Container(  
        ...  
      ),  
    ),  
  );  
}

img

img

img

img

img

img

img

img

flutter3实现直播功能

img

img

// flutter3直播模块  Q:282310962  

@override  
Widget build(BuildContext context) {  
  return Scaffold(  
    backgroundColor: Colors.black,  
    extendBodyBehindAppBar: true,  
    appBar: AppBar(  
      forceMaterialTransparency: true,  
      backgroundColor: Colors.black,  
      foregroundColor: Colors.white,  
      toolbarHeight: 0,  
    ),  
    body: Column(  
      children: [  
        Expanded(  
          child: Stack(  
            children: [  
              PageView.builder(  
                scrollBehavior: CustomScrollBehavior().copyWith(scrollbars: false),  
                scrollDirection: Axis.vertical,  
                controller: pageVerticalController,  
                onPageChanged: (index) async {  
                  setState(() {  
                    liveIndex = index;  
                  });  
                  player.stop();  
                  await player.open(Media(liveJson[index]['src']));  
                },  
                itemCount: liveJson.length,  
                itemBuilder: (context, index) {  
                  return Stack(  
                    children: [  
                      // 视频区域  
                      Positioned(  
                        ...  
                      ),  

                      /// 水平滚动模块(清屏/浮层)  
                      PageView(  
                        scrollDirection: Axis.horizontal,  
                        controller: pageHorizontalController,  
                        onPageChanged: (index) {  
                          // ...  
                        },  
                        children: [  
                          // 直播清屏  
                          Container(  
                            ...  
                          ),  
                          // 直播浮层  
                          Stack(  
                            children: [  
                              // 顶部区域  
                              Positioned(  
                                top: MediaQuery.of(context).padding.top + 7,  
                                left: 10.0,  
                                right: 0,  
                                child: Column(  
                                  crossAxisAlignment: CrossAxisAlignment.start,  
                                  children: [  
                                    // 直播间头像  
                                    Container(  
                                      ...  
                                    ),  
                                    // 排名统计  
                                    Container(  
                                      ...  
                                    ),  
                                    // 红包活动  
                                    Container(  
                                      ...  
                                    ),  
                                  ],  
                                ),  
                              ),  
                              // 底部区域  
                              Positioned(  
                                child: Column(  
                                  crossAxisAlignment: CrossAxisAlignment.start,  
                                  children: [  
                                    // 商品购买动效  
                                    Container(  
                                      ...  
                                    ),  

                                    // 送礼物动效  
                                    AnimationLiveGift(  
                                      giftQueryList: [  
                                        {'label': '小心心', 'gift': 'assets/images/gift/gift1.png', 'user': 'Jack', 'avatar': 'assets/images/avatar/img02.jpg', 'num': 12},  
                                        {'label': '棒棒糖', 'gift': 'assets/images/gift/gift2.png', 'user': 'Andy', 'avatar': 'assets/images/avatar/img06.jpg', 'num': 36},  
                                        {'label': '大啤酒', 'gift': 'assets/images/gift/gift3.png', 'user': '一条咸鱼', 'avatar': 'assets/images/avatar/img01.jpg', 'num': 162},  
                                        ...  
                                      ],  
                                    ),  

                                    // 加入直播间动效  
                                    AnimationLiveJoin(  
                                      joinQueryList: [  
                                        {'avatar': 'assets/images/logo.png', 'name': 'andy'},  
                                        {'avatar': 'assets/images/logo.png', 'name': 'jack'},  
                                        ...  
                                      ],  
                                    ),  

                                    // 直播弹幕+商品讲解  
                                    Container(  
                                      margin: EdgeInsets.only(top: 7.0),  
                                      height: 200.0,  
                                      child: Row(  
                                        ...  
                                      ),  
                                    ),  

                                    // 底部工具栏  
                                    Container(  
                                      margin: const EdgeInsets.only(top: 7.0),  
                                      child: Row(  
                                        ...  
                                      ),  
                                    ),  
                                  ],  
                                ),  
                              ),  
                            ],  
                          ),  
                        ],  
                      ),  
                    ],  
                  );  
                },  
              ),  
            ],  
          ),  
        ),  
      ],  
    ),  
  );  
}

以上就是flutter3.27.1实战抖音app商城的一些知识分享,整个项目涉及到知识点蛮多。希望以上分享对小伙伴们有些帮助哈~~

往期热文

https://segmentfault.com/a/1190000045042968
https://segmentfault.com/a/1190000045245775
https://segmentfault.com/a/1190000045381943
https://segmentfault.com/a/1190000045556523
https://segmentfault.com/a/1190000045667190

作者:xiaoyan2017
链接: https://segmentfault.com/a/1190000046075489
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

收起阅读 »

plus.webview.create创建的 webview如何监听h5发送的消息

目前采用的是讨巧的方法
h5页面修改title
document.title = id '|' type;

webview页面监听titleUpdate事件

            var w = plus.webview.create(this.wvSrc, 'id',   
            {   
                top: uni.getSystemInfoSync().statusBarHeight   50,    
                height: uni.getSystemInfoSync().windowHeight - 95 - uni.getSystemInfoSync().statusBarHeight    
            },  
            {    
                preload: 'preload webview'    
            });  
            //监听titleUpdate  
            w.addEventListener('titleUpdate', (e) => {  
                let o = e.title.split("|")[0]  
                if(isFinite(o)){  
                    this.toBBsDetail(o)  
                }  
            });  

            var currentWebview = this.$mp.page.$getAppWebview()     
            currentWebview.append(w);  

不知道还有其他方法没

继续阅读 »

目前采用的是讨巧的方法
h5页面修改title
document.title = id '|' type;

webview页面监听titleUpdate事件

            var w = plus.webview.create(this.wvSrc, 'id',   
            {   
                top: uni.getSystemInfoSync().statusBarHeight   50,    
                height: uni.getSystemInfoSync().windowHeight - 95 - uni.getSystemInfoSync().statusBarHeight    
            },  
            {    
                preload: 'preload webview'    
            });  
            //监听titleUpdate  
            w.addEventListener('titleUpdate', (e) => {  
                let o = e.title.split("|")[0]  
                if(isFinite(o)){  
                    this.toBBsDetail(o)  
                }  
            });  

            var currentWebview = this.$mp.page.$getAppWebview()     
            currentWebview.append(w);  

不知道还有其他方法没

收起阅读 »

uniapp中 自身调用自身 递归调用自己

<view class="">  
    <template     v-for="(secondPl,index) in secondComments">  
        <view class="leavelMessageRember-item1"   >  
            <view class="contentName">  
                {{secondPl.userName}}&nbsp;回复&nbsp;@{{parentName}}  
            </view>  
            <view class="content">  
                {{secondPl.message}}   
            </view>   
        </view>  
        <view class=""    @click="shuouhui" v-if="!secondPl.children" style="padding-left: 5px;color: #aaaaaa;background-color: aliceblue;font-size: 12px;">  
            收回  
        </view>  
        <morePL  
            @Tback="shuouhui"     
            :parentName='secondPl.userName'  
            :secondComments="secondPl.children"  
        ></morePL>  
    </template>  
</view>
继续阅读 »
<view class="">  
    <template     v-for="(secondPl,index) in secondComments">  
        <view class="leavelMessageRember-item1"   >  
            <view class="contentName">  
                {{secondPl.userName}}&nbsp;回复&nbsp;@{{parentName}}  
            </view>  
            <view class="content">  
                {{secondPl.message}}   
            </view>   
        </view>  
        <view class=""    @click="shuouhui" v-if="!secondPl.children" style="padding-left: 5px;color: #aaaaaa;background-color: aliceblue;font-size: 12px;">  
            收回  
        </view>  
        <morePL  
            @Tback="shuouhui"     
            :parentName='secondPl.userName'  
            :secondComments="secondPl.children"  
        ></morePL>  
    </template>  
</view>
收起阅读 »

浏览器信息检测小工具

浏览器

https://passer-by.com/browser/

进入上方网页可以检测浏览器信息

https://passer-by.com/browser/

进入上方网页可以检测浏览器信息

HBuliderX编译器代码折叠BUG

bug反馈

我最近使用HBuliderX写uniapp小程序的css样式时发现一个比较严重的BUG,在折叠的代码下面继续写新的同级的样式的时候再次展开前面的那个折叠的样式时,会自动将刚刚写好的css样式加入到之前折叠的那个样式当中去,语法是scss。请官方大大能够修复一下或者给个答复,这是编译器特性吗,还是什么。

继续阅读 »

我最近使用HBuliderX写uniapp小程序的css样式时发现一个比较严重的BUG,在折叠的代码下面继续写新的同级的样式的时候再次展开前面的那个折叠的样式时,会自动将刚刚写好的css样式加入到之前折叠的那个样式当中去,语法是scss。请官方大大能够修复一下或者给个答复,这是编译器特性吗,还是什么。

收起阅读 »

2025社区交流小区校园圈子模板

微信小程序

https://ext.dcloud.net.cn/plugin?id=21634

https://ext.dcloud.net.cn/plugin?id=21634

midButton按钮onTabBarMidButtonTap点击事件弹出弹窗

在使用midButton做凸起按钮的时候,在App.vue里使用uni.onTabBarMidButtonTap来监听点击事件,网上都是点击后执行的跳转页面,我想点击后出现弹窗。
把弹窗组件写在App.vue里,代码
<template>
<view>
<customDialog />
<router-view></router-view>
</view>
</template>
不使用router-view页面就是空的,使用router-view下方的tabBar就没了。
应该怎么处理呢?

继续阅读 »

在使用midButton做凸起按钮的时候,在App.vue里使用uni.onTabBarMidButtonTap来监听点击事件,网上都是点击后执行的跳转页面,我想点击后出现弹窗。
把弹窗组件写在App.vue里,代码
<template>
<view>
<customDialog />
<router-view></router-view>
</view>
</template>
不使用router-view页面就是空的,使用router-view下方的tabBar就没了。
应该怎么处理呢?

收起阅读 »

APP 什么时候支持requestMerchantTransfer 微信确认收款。 现在APP 卡主了

APP 什么时候支持requestMerchantTransfer 微信确认收款。 现在APP 卡主了
APP 什么时候支持requestMerchantTransfer 微信确认收款。 现在APP 卡主了
APP 什么时候支持requestMerchantTransfer 微信确认收款。 现在APP 卡主了
APP 什么时候支持requestMerchantTransfer 微信确认收款。 现在APP 卡主了
APP 什么时候支持requestMerchantTransfer 微信确认收款。 现在APP 卡主了
APP 什么时候支持requestMerchantTransfer 微信确认收款。 现在APP 卡主了
APP 什么时候支持requestMerchantTransfer 微信确认收款。 现在APP 卡主了
APP 什么时候支持requestMerchantTransfer 微信确认收款。 现在APP 卡主了

继续阅读 »

APP 什么时候支持requestMerchantTransfer 微信确认收款。 现在APP 卡主了
APP 什么时候支持requestMerchantTransfer 微信确认收款。 现在APP 卡主了
APP 什么时候支持requestMerchantTransfer 微信确认收款。 现在APP 卡主了
APP 什么时候支持requestMerchantTransfer 微信确认收款。 现在APP 卡主了
APP 什么时候支持requestMerchantTransfer 微信确认收款。 现在APP 卡主了
APP 什么时候支持requestMerchantTransfer 微信确认收款。 现在APP 卡主了
APP 什么时候支持requestMerchantTransfer 微信确认收款。 现在APP 卡主了
APP 什么时候支持requestMerchantTransfer 微信确认收款。 现在APP 卡主了

收起阅读 »

【记录】vue2--->vue3组合式

vue3

1、this.$nextTick,替换方案如下:

import { nextTick} from "vue";  
nextTick(function() {})

2、uni.createSelectorQuery().in(this),其他相似api需要this应该也是一样的,替换方案如下:

uniapp文档示例(选择组合式api):https://uniapp.dcloud.net.cn/api/ui/nodes-info.html#selectorquery-in

import { getCurrentInstance } from "vue";  
const instance = getCurrentInstance();  
const that = instance.proxy;  
const query = uni.createSelectorQuery().in(that);

评论区”朱小“的发问,加.in(that)和不加的区别。以下是查找到的内容,仅供参考:

3、子组件使用this.$parent,替换方案:props、provide/inject、emits或事件总线

4、子组件使用this.$root.$on("hook:onShow", () => {...相关逻辑})监听父组件onShow生命周期,替换方案如下:

import { onShow } from "@dcloudio/uni-app";  
onShow(() => {  
    ...相关逻辑  
})
继续阅读 »

1、this.$nextTick,替换方案如下:

import { nextTick} from "vue";  
nextTick(function() {})

2、uni.createSelectorQuery().in(this),其他相似api需要this应该也是一样的,替换方案如下:

uniapp文档示例(选择组合式api):https://uniapp.dcloud.net.cn/api/ui/nodes-info.html#selectorquery-in

import { getCurrentInstance } from "vue";  
const instance = getCurrentInstance();  
const that = instance.proxy;  
const query = uni.createSelectorQuery().in(that);

评论区”朱小“的发问,加.in(that)和不加的区别。以下是查找到的内容,仅供参考:

3、子组件使用this.$parent,替换方案:props、provide/inject、emits或事件总线

4、子组件使用this.$root.$on("hook:onShow", () => {...相关逻辑})监听父组件onShow生命周期,替换方案如下:

import { onShow } from "@dcloudio/uni-app";  
onShow(() => {  
    ...相关逻辑  
})
收起阅读 »

小程序版本

<template>
<view class="tagBall">
<view class="tag" v-for="(item, index) in texts" :key="index" :style="[getTagStyle(index)]">
<view>{{item.userName}}</view>
<view class="dot"></view>
</view>
</view>
</template>
<script>
export default {
props: {
speed: {
type: Number,
default: 0.5
},
texts: {
type: Array,
default: () => {
return [];
}
}
},
data() {
return {
tagEle: [],
RADIUS: 150,
fallLength: 240,
angleX: Math.PI / 400,
angleY: Math.PI / 400,
tags: [],
liviews: [],
CX: 0,
CY: 0,
timer: null,
clickX: 0,
clickY: 0
};
},
computed: {
getTagStyle() {
return (index) => {
const style = this.liviews[index] || {};
return {
fontSize: ${style.fontSize},
opacity: style.opacity,
transition: style.transition,
filter: style.filter,
zIndex: style.zIndex,
transformStyle: style.transformStyle,
left: ${style.left},
top: ${style.top},
perspective: '20000rpx',
transition: 'all linear 0.1s',
transform: style.transform,
};
};
}
},
destroyed() {
this.clearTimer();
},
mounted() {
this.initTags();
},
methods: {
clearTimer() {
if (this.timer) {
clearInterval(this.timer);
this.timer = null;
}
},
getTags(option) {
this.tagEle = option.tagEle;
this.CX = option.CX;
this.CY = option.CY;
this.init();
},
rotateX() {
const cos = Math.cos(this.angleX this.speed);
const sin = Math.sin(this.angleX
this.speed);
for (let i = 0; i < this.tags.length; i++) {
let t = this.tags[i];
const y1 = t.y cos + t.z sin;
const z1 = t.z cos - t.y sin;
t.y = y1;
t.z = z1;
}
},
rotateY() {
const cos = Math.cos(this.angleY this.speed);
const sin = Math.sin(this.angleY
this.speed);
for (let i = 0; i < this.tags.length; i++) {
let t = this.tags[i];
const x1 = t.x cos - t.z sin;
const z1 = t.z cos + t.x sin;
t.x = x1;
t.z = z1;
}
},
init() {
this.tags = [];
this.liviews = [];
for (let i = 0; i < this.tagEle.length; i++) {
const k = (2 (i + 1) - 1) / this.tagEle.length - 1;
const a = Math.acos(k);
const b = a
Math.sqrt(this.tagEle.length Math.PI);
const x = this.RADIUS
Math.sin(a) Math.cos(b);
const y = this.RADIUS
Math.sin(a) Math.sin(b);
const z = this.RADIUS
Math.cos(a);
const t = this.tag(this.tagEle[i], x / 2, y / 2, z / 2);
this.tags.push(t);
}
this.animate(this.tags);
},
tag(ele, x, y, z) {
return {
ele,
x,
y,
z
};
},
move(t, i) {
const scale = this.fallLength / (this.fallLength - t.z 1.3);
const alpha = (t.z + this.RADIUS) / (2
this.RADIUS);
this.liviews.push({
fontSize: 5 scale + 'px',
opacity: alpha + 0.5,
transition: 'all linear 0.5s',
filter: 'alpha(opacity=' + (alpha + 0.5)
100 + ')',
zIndex: parseInt(scale 100),
transformStyle: 'preserve-3d',
left: t.x + this.CX - t.ele.offsetWidth / 2 + 'px',
top: t.y + this.CY - t.ele.offsetHeight / 2 + 'px',
perspective: '2000rpx',
transformStyle: 'preserve-3d',
transform: scale(${alpha+0.5}),
});
},
animate(x) {
this.clearTimer();
this.timer = setInterval(() => {
this.rotateX();
this.rotateY();
this.liviews = [];
for (let i = 0; i < x.length; i++) {
this.move(x[i], i);
}
}, 25)
},
touchstartscene(e) {
this.clickX = e.touches[0].clientX;
this.clickY = e.touches[0].clientY;
this.clearTimer();
},
touchendscene() {
this.animate(this.tags);
this.clearTimer();
},
touchmovescene(e) {
const fx = this.getDirection(this.clickX, this.clickY, e.touches[0].clientX, e.touches[0].clientY);
let x = this.clickX - e.touches[0].clientX - this.CX;
let y = this.clickY - e.touches[0].clientY - this.CY;
if (fx === 1) {
x = e.touches[0].clientX - this.clickX;
y = e.touches[0].clientY - this.clickY - this.CY;
} else if (fx === 2) {
x = e.touches[0].clientX - this.clickX;
y = e.touches[0].clientY + this.CY;
} else if (fx === 3) {
x = this.clickX + e.touches[0].clientX + this.CX;
y = this.clickY - e.touches[0].clientY - this.CY;
} else {
x = this.clickX - e.touches[0].clientX - this.CX;
y = this.clickY - e.touches[0].clientY - this.CY;
}
this.angleY = x
0.0001;
this.angleX = y 0.0001;
this.rotateX();
this.rotateY();
this.liviews = [];
for (let i = 0; i < this.tags.length; i++) {
this.move(this.tags[i], i);
}
},
getDirection(startx, starty, endx, endy) {
const angx = endx - startx;
const angy = endy - starty;
let result = 0;
if (Math.abs(angx) < 2 && Math.abs(angy) < 2) {
return result;
}
const angle = Math.atan2(angy, angx)
180 / Math.PI;
if (angle >= -135 && angle <= -45) {
result = 1;
} else if (angle > 45 && angle < 135) {
result = 2;
} else if ((angle >= 135 && angle <= 180) || (angle >= -180 && angle < -135)) {
result = 3;
} else if (angle >= -45 && angle <= 45) {
result = 4;
}
return result;
},
initTags() {
let tagEles = [];
this.$nextTick(() => {
const query = uni.createSelectorQuery().in(this);
query.selectAll('.tag').boundingClientRect(rects => {
tagEles = rects.map(rect => ({
offsetWidth: rect.width,
offsetHeight: rect.height
}));
}).exec();
query.select('.tagBall').boundingClientRect(rect => {
const CX = rect.width / 2;
const CY = rect.height / 2;
this.getTags({
tagEle: tagEles,
CX,
CY
});
}).exec();
});
}
}
};
</script>
<style scoped>
.tagBall {
height: 214rpx;
position: relative;
perspective: 2000rpx;
transform-style: preserve-3d;
left: 0;
top: 0;
transition: all linear 0.1s;
margin-left: 40rpx;
}

.tag {  
    width: 150rpx;  
    position: absolute;  
    font-weight: 500;  
    font-size: 12rpx;  
    color: #000000;  
    display: flex;  
    flex-direction: column;  
    align-items: center;  
    transition: all ease-in-out 0.3s;  
}  

.dot {  
    width: 16rpx;  
    height: 16rpx;  
    background: #FCC928;  
    border-radius: 50%;  
    margin-top: 10rpx;  
}  

</style>

继续阅读 »

<template>
<view class="tagBall">
<view class="tag" v-for="(item, index) in texts" :key="index" :style="[getTagStyle(index)]">
<view>{{item.userName}}</view>
<view class="dot"></view>
</view>
</view>
</template>
<script>
export default {
props: {
speed: {
type: Number,
default: 0.5
},
texts: {
type: Array,
default: () => {
return [];
}
}
},
data() {
return {
tagEle: [],
RADIUS: 150,
fallLength: 240,
angleX: Math.PI / 400,
angleY: Math.PI / 400,
tags: [],
liviews: [],
CX: 0,
CY: 0,
timer: null,
clickX: 0,
clickY: 0
};
},
computed: {
getTagStyle() {
return (index) => {
const style = this.liviews[index] || {};
return {
fontSize: ${style.fontSize},
opacity: style.opacity,
transition: style.transition,
filter: style.filter,
zIndex: style.zIndex,
transformStyle: style.transformStyle,
left: ${style.left},
top: ${style.top},
perspective: '20000rpx',
transition: 'all linear 0.1s',
transform: style.transform,
};
};
}
},
destroyed() {
this.clearTimer();
},
mounted() {
this.initTags();
},
methods: {
clearTimer() {
if (this.timer) {
clearInterval(this.timer);
this.timer = null;
}
},
getTags(option) {
this.tagEle = option.tagEle;
this.CX = option.CX;
this.CY = option.CY;
this.init();
},
rotateX() {
const cos = Math.cos(this.angleX this.speed);
const sin = Math.sin(this.angleX
this.speed);
for (let i = 0; i < this.tags.length; i++) {
let t = this.tags[i];
const y1 = t.y cos + t.z sin;
const z1 = t.z cos - t.y sin;
t.y = y1;
t.z = z1;
}
},
rotateY() {
const cos = Math.cos(this.angleY this.speed);
const sin = Math.sin(this.angleY
this.speed);
for (let i = 0; i < this.tags.length; i++) {
let t = this.tags[i];
const x1 = t.x cos - t.z sin;
const z1 = t.z cos + t.x sin;
t.x = x1;
t.z = z1;
}
},
init() {
this.tags = [];
this.liviews = [];
for (let i = 0; i < this.tagEle.length; i++) {
const k = (2 (i + 1) - 1) / this.tagEle.length - 1;
const a = Math.acos(k);
const b = a
Math.sqrt(this.tagEle.length Math.PI);
const x = this.RADIUS
Math.sin(a) Math.cos(b);
const y = this.RADIUS
Math.sin(a) Math.sin(b);
const z = this.RADIUS
Math.cos(a);
const t = this.tag(this.tagEle[i], x / 2, y / 2, z / 2);
this.tags.push(t);
}
this.animate(this.tags);
},
tag(ele, x, y, z) {
return {
ele,
x,
y,
z
};
},
move(t, i) {
const scale = this.fallLength / (this.fallLength - t.z 1.3);
const alpha = (t.z + this.RADIUS) / (2
this.RADIUS);
this.liviews.push({
fontSize: 5 scale + 'px',
opacity: alpha + 0.5,
transition: 'all linear 0.5s',
filter: 'alpha(opacity=' + (alpha + 0.5)
100 + ')',
zIndex: parseInt(scale 100),
transformStyle: 'preserve-3d',
left: t.x + this.CX - t.ele.offsetWidth / 2 + 'px',
top: t.y + this.CY - t.ele.offsetHeight / 2 + 'px',
perspective: '2000rpx',
transformStyle: 'preserve-3d',
transform: scale(${alpha+0.5}),
});
},
animate(x) {
this.clearTimer();
this.timer = setInterval(() => {
this.rotateX();
this.rotateY();
this.liviews = [];
for (let i = 0; i < x.length; i++) {
this.move(x[i], i);
}
}, 25)
},
touchstartscene(e) {
this.clickX = e.touches[0].clientX;
this.clickY = e.touches[0].clientY;
this.clearTimer();
},
touchendscene() {
this.animate(this.tags);
this.clearTimer();
},
touchmovescene(e) {
const fx = this.getDirection(this.clickX, this.clickY, e.touches[0].clientX, e.touches[0].clientY);
let x = this.clickX - e.touches[0].clientX - this.CX;
let y = this.clickY - e.touches[0].clientY - this.CY;
if (fx === 1) {
x = e.touches[0].clientX - this.clickX;
y = e.touches[0].clientY - this.clickY - this.CY;
} else if (fx === 2) {
x = e.touches[0].clientX - this.clickX;
y = e.touches[0].clientY + this.CY;
} else if (fx === 3) {
x = this.clickX + e.touches[0].clientX + this.CX;
y = this.clickY - e.touches[0].clientY - this.CY;
} else {
x = this.clickX - e.touches[0].clientX - this.CX;
y = this.clickY - e.touches[0].clientY - this.CY;
}
this.angleY = x
0.0001;
this.angleX = y 0.0001;
this.rotateX();
this.rotateY();
this.liviews = [];
for (let i = 0; i < this.tags.length; i++) {
this.move(this.tags[i], i);
}
},
getDirection(startx, starty, endx, endy) {
const angx = endx - startx;
const angy = endy - starty;
let result = 0;
if (Math.abs(angx) < 2 && Math.abs(angy) < 2) {
return result;
}
const angle = Math.atan2(angy, angx)
180 / Math.PI;
if (angle >= -135 && angle <= -45) {
result = 1;
} else if (angle > 45 && angle < 135) {
result = 2;
} else if ((angle >= 135 && angle <= 180) || (angle >= -180 && angle < -135)) {
result = 3;
} else if (angle >= -45 && angle <= 45) {
result = 4;
}
return result;
},
initTags() {
let tagEles = [];
this.$nextTick(() => {
const query = uni.createSelectorQuery().in(this);
query.selectAll('.tag').boundingClientRect(rects => {
tagEles = rects.map(rect => ({
offsetWidth: rect.width,
offsetHeight: rect.height
}));
}).exec();
query.select('.tagBall').boundingClientRect(rect => {
const CX = rect.width / 2;
const CY = rect.height / 2;
this.getTags({
tagEle: tagEles,
CX,
CY
});
}).exec();
});
}
}
};
</script>
<style scoped>
.tagBall {
height: 214rpx;
position: relative;
perspective: 2000rpx;
transform-style: preserve-3d;
left: 0;
top: 0;
transition: all linear 0.1s;
margin-left: 40rpx;
}

.tag {  
    width: 150rpx;  
    position: absolute;  
    font-weight: 500;  
    font-size: 12rpx;  
    color: #000000;  
    display: flex;  
    flex-direction: column;  
    align-items: center;  
    transition: all ease-in-out 0.3s;  
}  

.dot {  
    width: 16rpx;  
    height: 16rpx;  
    background: #FCC928;  
    border-radius: 50%;  
    margin-top: 10rpx;  
}  

</style>

收起阅读 »

【TuiPlus】简洁高效 uniapp x UI组件库重磅发布,原生uts插件加持,保障流畅体验,助力快速开发,打造优质原生应用。

原生 图表 UI uts

项目介绍

> TuiPlus UI组件库 —— 全面兼容最新设计理念。本次重构不仅着眼于功能的提升,更深入探索用户体验的无限可能。我们的组件库致力于打造一个既全面又精致的UI设计平台,强调设计的精致度和扩展性,始终将用户体验放在首位。在细节的打磨上,我们精益求精,自豪地呈现——TuiPlus UI组件库。

> 创新与差异 —— 新版增加了UTS版图表,裁剪等众多组件,我们的专注点在于提供丰富且独特的组件。每一款组件都是经过匠心独运的设计和优化,确保性能卓越。特别是我们的UTS图表组件,采用高性能的canvas技术,实现了无需webview嵌套的纯canvas绘制,带来了行业领先的流畅度和响应速度。TuiPlus UI组件库全面支持iOS、Android以及Web/H5三端,无缝适配多种屏幕尺寸和形态,包括长屏、宽屏、PC、折叠屏和横屏,满足不同场景下的多样化需求。

!视频

TuiPlus文档地址:https://life.yundie.xyz/t-uvue-ui/docs/index.html

功能特性快速了解

TuiPlus扫码下载体验

web在线体验

Image

安卓下载链接

Image

继续阅读 »

项目介绍

> TuiPlus UI组件库 —— 全面兼容最新设计理念。本次重构不仅着眼于功能的提升,更深入探索用户体验的无限可能。我们的组件库致力于打造一个既全面又精致的UI设计平台,强调设计的精致度和扩展性,始终将用户体验放在首位。在细节的打磨上,我们精益求精,自豪地呈现——TuiPlus UI组件库。

> 创新与差异 —— 新版增加了UTS版图表,裁剪等众多组件,我们的专注点在于提供丰富且独特的组件。每一款组件都是经过匠心独运的设计和优化,确保性能卓越。特别是我们的UTS图表组件,采用高性能的canvas技术,实现了无需webview嵌套的纯canvas绘制,带来了行业领先的流畅度和响应速度。TuiPlus UI组件库全面支持iOS、Android以及Web/H5三端,无缝适配多种屏幕尺寸和形态,包括长屏、宽屏、PC、折叠屏和横屏,满足不同场景下的多样化需求。

!视频

TuiPlus文档地址:https://life.yundie.xyz/t-uvue-ui/docs/index.html

功能特性快速了解

TuiPlus扫码下载体验

web在线体验

Image

安卓下载链接

Image

收起阅读 »