0%

React native(0.71) 当前还没有官方的Drag and Drop的支持。最近的项目中需要在客户端上用类似Drag and Drop这样的操作来改善一个功能的交互效果。这里记录一下是如何通过 Reanimated 和 gesture-handler 实现这个功能的。

实现Drag and Drop的交互主要需要解决以下几个问题:

  1. 如何在Drag时实现View跟随touch的轨迹移动?

  2. 接受Drop的View如何感知Dragging的轨迹到达了该View的区域内?

  3. 如何在Drop操作时将Drag和Drop的对象传递给 parent

    下面将分别说明这几个问题是如何解决的

Drag的效果

react-native-gesture-handler里,我们可以通过Pan gesture实现Drag的效果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
const positionX = useSharedValue(0);
const positionY = useSharedValue(0);

const draggingGesture = Gesture.Pan()
.maxPointers(1)
.activateAfterLongPress(100)
.onStart(() => {
active.value = true;
})
.onUpdate((e) => {
positionX.value = e.translationX;
positionY.value = e.translationY;

})
.onEnd((e) => {

positionX.value = withTiming(0);
positionY.value = withTiming(0);
}
active.value = false;

});


我们通过两个SharedValue来记录touch的轨迹,然后通过AnimatedStyle对控件的位置进行修改,就达到了Drag时View跟随的效果:

1
2
3
4
5
6
7
8
9
10
const dragStyle = useAnimatedStyle(() => {
return {
opacity: active.value ? 0.5 : 1,
borderColor: active.value ? 'red' : 'transparent',
transform: [
{ translateX: positionX.value },
{ translateY: positionY.value },
],
};
});

Drop Zone的进入感知

对于可以Drop的区域,当Dragging进入该控件时,提供一个highlight的效果让用户知道操作可以进行是一个比较好的用户交互。Drop Zone通常和Draggable的View并不存在包含隶属的关系,所以我们需要让它们有一个共同的parent的控件来传递dragging时的touch所在的位置信息。

1
2
3
4
5
6
const draggingPos = useSharedValue({ x: -1, y: -1 });

const tracking = (x, y) => {
//console.log(x, y)
draggingPos.value = { x, y };
};

然后在draggingGesture的onUpdate中更新位置信息:

1
runOnJS(tracking)(e.absoluteX, e.absoluteY);

需要注意的是,这里需要用runOnJS来调用tracking。

有了位置信息,我们还需要知道Drop Zone的绝对位置信息,可以通过view的onLayout的调用时获取

1
2
3
4
5
6
const onLayout = () => {
viewRef.current?.measureInWindow((x, y, width, height) => {
viewRect.value = { x, y, width, height };

});
};

这样我们就可以在Drop Zone中检测dragging的touch是否进入到Drop Zone的内部,并通过animatedStyle实时改变自己的状态用于通知用户

1
2
3
4
5
6
7
8
9
10
11
12
13
//Detect the dragging pointer is inside this zone or not
const draggingInView = useDerivedValue(() => {
return isInside(draggingPos.value.x, draggingPos.value.y, viewRect.value);
}, [draggingPos, viewRect]);

const zoneStyle = useAnimatedStyle(() => {
return {
borderColor: draggingInView.value ? "red" : "gray",
borderWidth: 2,
borderRadius: 5,
borderStyle: "dashed"
}
})

Drop的事件处理

最后,我们需要处理Drop的事件。Drop的事件是由draggingGesture来触发的,在其onEnd的方法中,我们可以检查此时touch是否处于drop zone中,如果在drop zone中则触发drop事件。

  1. 在onLayout新增一个registerView, 将当前Drop zone的位置和区域大小的信息传递给父类

    1
    registerView(id, x, y, width, height);
  2. 修改后的draggingGesture如下,在onEnd中新增dragAndDrop的调用

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    const draggingGesture = Gesture.Pan()
    .maxPointers(1)
    .activateAfterLongPress(100)
    .onStart(() => {
    active.value = true;
    })
    .onUpdate((e) => {
    positionX.value = e.translationX;
    positionY.value = e.translationY;
    runOnJS(tracking)(e.absoluteX, e.absoluteY);
    })
    .onEnd((e) => {
    if (!runOnJS(dragAndDrop)(e.absoluteX, e.absoluteY, id)) {
    positionX.value = withTiming(0);
    positionY.value = withTiming(0);
    }
    active.value = false;
    runOnJS(tracking)(-1, -1)
    });
  3. 由父类检测是否处于drop zone,并调用onDrop

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    const dragAndDrop = (x, y, id) => {
    const view = viewRegister.value.find(({rect}) => {
    return isInside(x, y, rect)
    })
    if (view) {
    onDrop(id, view.id)
    return true
    }

    return false;
    };

示例

春节假期的最后一天和家人一起去观看了《流浪地球2》。小时候我是半个科幻迷,这部影片在10分制中我可以给它9分,这是一部值得在去电影院里观看的电影。
在这里我仅仅想写下对这部巨作不太喜欢的地方。

主要是周喆直这个角色,让我不大舒服。并不是李雪健老师的演技不好,单纯是我不太喜欢这个角色。无论是这个角色的造型还是气质,周喆直都太像周恩来了。
我觉得这是剧组刻意把这个角色塑造成周恩来式的人物。我并不是讨厌周恩来,周总理在他所处的那个时代已经做的足够优秀,但是如果现在或未来我们还需要周恩来的话,我会觉得有点悲哀。

在电影中,周喆直是中国团队的核心,或者说他是中国团队中所有人的家长,而且是说一不二的那种家长。在中国的团队中,除了他以外,没有出现其他和他同龄
的人,所以在他面前,其他人都显得比较稚嫩。其中一个关于演讲的情节,在电影中他退休后还重现了一遍:“这里哪一次演讲不重要呀?一字不落的念下来”。

电影中的中国团队是一个典型的中国式团队,团队中有一个领导,其他人都是领导的副手(工具人)。这种团队模式正是让我不太舒服的地方,因为我认为这种团队模式
并不是一种优秀的团队模式,因为他完全依赖领导人的个人能力。当领导人犯错时,将没有弥补的机会。如果承认错误的代价特别大的话,那么就只有一条道走到黑了。

在这种团队中有一个好处,领导说什么就做什么,不需要自己去思考,也许还是有很多人喜欢这种团队吧。

春节前带着一家人一起去了趟重庆游玩,回来正好赶上过年,年过完了才有点时间把重庆的旅行记录下来。

这并不是我第一次来重庆,上一次还是5年前的国庆,因没有买到直达的车票而选择在重庆转乘。那一次的晚上带着家人来到洪崖洞看看,没想到在入口处便是人贴人的缓慢挪步,赶紧在不太拥挤的街边摊吃了碗小面就坐车返回住处,没能好好的看看。所以这一次规划了3晚4天,足足有两天的时间可以在市区里转转。出发时已是傍晚,抵达酒店时差不多晚上10点左右。酒店离江边不远,是一栋比较老的建筑,整个酒店是按民国风装修,原先是重庆日报的旧址,旁边就是蒋介石的行宫。出发时没吃晚饭,到酒店放好行李就下楼找吃的东西。重庆的特色是火锅,酒店不远就有一家,但是除了妻子外,孩子和我都不吃辣,所以还是在旁边的馆子里点了面和饺子解决了。

去重庆前在网上找了些旅游的攻略,选了两条路线打印好带在身上。第二天吃完早饭,便朝着第一个目的地“解放碑”出发。从酒店出发,绕过蒋介石的行宫就是凯旋门。凯旋门大概百来阶台阶,是以前一条连接上城区和下城区的阶梯。为了节省体力,我们坐旁边的电梯上去。出电梯门后,看到的仍然是车水马龙的景色,这是重庆的一大特色。按导航走不到1公里远便到了解放碑。

解放碑算是重庆的市中心之一吧,现在的重庆太大了。可能是还未到春节的缘故,解放碑附近的商业街上并没有多少人,和上一次来的时候大相径庭,正适合我们这种想闲逛的。在解放碑稍微休息便朝着洪崖洞方向走去,路过WFC观景台,便想带家人上去看看。走到前台处得知门票要100元每人,而且现在的天气不怎么好,上去看不到什么风景,于是放弃。又走了一会,到达了洪崖洞,洪崖洞是面向嘉陵江沿着山崖而修建的一组民族风格的建筑群。三年的疫情对洪崖洞元气重伤,里面大部分店铺都没开门,游客了了无几。从顶层走到1楼,又从1楼走到顶楼,里面完全没有十多年前我第一次到重庆出差时的熙熙攘攘。时间已到午时,就在小什字附近找了一个面馆,还是重庆小面。下午坐地铁,去重庆自然博物馆。重庆太大了,在地铁上坐了一个多小时,下车后又打了一个车。和之前参观过的杭州自然博物馆相比,重庆自然博物馆的规模要稍微小一点。上次的杭州之行只带了姐姐,妹妹那时还太小,而这次来重庆妹妹已经上三年级。一般到一个城市旅行,如果有博物馆我都会带孩子去看看。重庆离自贡比较近,所以这里的镇馆之宝还是恐龙化石。每次在博物馆里参观时,我的思绪都会随着展品发散,脑海里总是想着它们在属于它们的时代里的场景。两个孩子倒是对这些展品没什么太大的兴趣,走马观花的草草看上一眼就朝下一个展品走去。1个小时左右,所有的展馆就看完了。自然博物馆周边没什么可以逛的,所以就坐地铁返回酒店。下了地铁出来,正好是重庆十八梯,穿过十八梯就离酒店不远了。天色慢慢的暗了下来,十八梯的灯火逐渐亮了起来。十八梯里的商铺与其他古镇之类的没什么差别,这也是中国古镇的一个通病,看几次就没太大的兴趣。走了一天的路,疲惫的没什么食欲,晚餐就在这步行街上吃了点醪糟汤圆。十八梯的人比白天其他地方的人都要多,让人感觉有点过节的氛围了。这里的建筑都是仿旧时的小楼,晚上在这里漫步还是有些感觉。

第三天早上起床,大家的元气都恢复了不少。这一天的计划是去李子坝附近,吃完早饭就出发去坐地铁。李子坝是重庆的网红景点,重庆的地貌是山多平地少,所以大部分建筑都需要依山而建。而地铁这种现代的交通工具,在重庆多半时候是轻轨,就是跑在路面高架桥上的。在李子坝站这里,地铁站和民居楼合为一体,所以就有了地铁从空中开进了楼里这一特色景象。在临江的街道上,专门修了一处平台供大家观赏。我们抵达时平台上已经挤满了人,大家都拿着手机相机等着下一班的地铁进楼。

李子坝不远,就是抗战遗址纪念馆。这里保留了一些民国时期的公馆,其实就是重庆陪都时期,达官显贵的住处活动场所。李子坝后面的山上,就是二厂文创公园,沿着导航的引导,开始往上爬。这种爬山的体验,正是我想让两个孩子来重庆体验的。路旁都是依山而建的小楼,虽然有些旧了但还是都有人住。爬山的时候我在想,在重庆这里真的不适合买车,山路多开一起来一会上一会下,路窄弯弯绕绕多,跟着导航走有时都会走错路。如果不是新修的有地下停车室的住宅,这车还不知道要停在哪里。也不由的佩服起重庆的人来,在这种不太适合人类居住的地方,建起这种繁华的都市来。重庆人的精神正如这生长在山崖上的大树,坚韧生生不息。

爬了好一会,终于抵达二厂。二厂是一个废弃的厂区,以前是国民党的中央印刷厂,主要负责印刷钞票。现在改成了一个文创公园,其实就是把一些符合文艺青年性质的小店聚焦在一起。以前的宿舍楼改出了3条步行街,在里面来来回回走了一圈,没留下什么印象。可能我已经过了那个容易被感到的年龄。在二厂的边上吃了午饭,还是吃的面。从二厂出来,下山的路要轻松很多,路上经过孙科公馆。和其他的民国的达官显贵不同,孙科公馆明显要寒酸一些,而且独独位于这半山腰上。和来时不同,李子坝站的另一个入口正好在山上,所以不用走那么远便坐上了地铁。

接下来,前往重庆的长江索道。长江索道曾经是重庆人平常所使用过江的交通工具,不知道什么时候成为了重庆的一张名片,成为大家到重庆后都有来看看的旅游景点,可能是在《疯狂的石头》里出境的而出名的缘故吧。从索道上下来便坐了一个公交车前往朝天门。朝天门现在是来福士广场,应该是重庆最大的商超,前几次来的时候还没有修好。在来福士,选择了一个重庆小吃店,终于没有再吃面了。

从来福士出来,沿着嘉陵江走不多远就是洪崖洞,晚上的洪崖洞比白天时好看多了。

第四天,重庆之旅便结束了。酒店外的面馆已经关门,听酒店前台说晚上重庆会有烟花秀,可惜来不及看了。还有很多地方想去看看的,两天的时间对重庆这么大的城市来说的确太短了点。

是的,这是我这十几年来的第一篇Blog。曾经在大学时期在博客中国上写过几篇Blog,但工作之后就再也没有写过任何的文字。原因有很多,但最主要的原因是我从来都是不善于表达,从小学上学开始写作文对我来说就是噩梦一般的存在。我很羡慕那些善于写文字的人,他们写出一篇好的文章就像喝水一样轻松。那为什么现在又要开始写Blog呢?原因是我想对我的生活做出一些改变。在十几年前第一次找工作时,我在简历上写过一句话:“可平凡不可平庸,不求辉煌但求出色”。如果现在不做点什么改变的话,恐怕我就会成为我最不想的碌碌无为的人。我希望从这一天开始,每一周都要完成一篇Blog,无论我多么害怕写文字这件事。不能再像过去那样,思考的只有碎片。需要通过写下来这种方式,让思考沉淀下来而不是遗忘。

2023年刚开始一周多点,这第一篇Blog里正好对过去的2022年做一个回顾。

  1. CKA认证

    2022年完成的第一个重要的任务是通过了CKA的认证考试。从完全不了解Kubernetes到通过CKA认证大概花了两周多的时间。主要是平常做运维的工作比较多,所以Kubernetes里的那些概念学起来并没有觉得太困难。Kubernetes对小公司而言还是太复杂了点,对我现在的公司里的项目而言,docker compose是更合适的选择。但对大型的公司和实施了微服务的项目而言,Kubernetes无疑已成为这个领域的标准。虽然通过了Kubernetes的CKA考试,但感觉这个认证对Kubernetes而言仅仅只能算是一个入门,今年仍然需要花一部分时间在这个领域的学习上,计划在今年的上半年通过CKS的认证。另外,还需要学习Helm、Operator、Prometheus等技术。

  2. 算法和数据结构的学习

    虽然我在Golang刚诞生时就学过,但一直没有大规模的使用过,这段时间的工作中又需要使用到,阅读代码略感吃力。想着干脆再学一遍,通过刷leetcode来学习Golang,这样一举两得。没想到这个过程暴露了我自己只是一个水货程序员的事实:Easy级别的还好,Medium级别偶尔会卡住,Hard级别的就刷不动了,按我的Leetcode排名 ,我在全球基本上算是比较差的那一档了,亏我还曾经顶着架构师的头衔工作了三年,简直脸都丢光了。回想了一下,算法和数据结构我只在上大学时学习过,工作后基本上就是个CRUD的搬砖工。我想如果我还要继续在这个行业里混的话,这个基础的东西是不是得补补。

    在Coursera上找到 Algorithms 这个课程,可惜我的进度没有跟上,只学到中途就放弃了。无论是内容的深度还是作业的严谨度都让我大为震撼,没有完全学下来对我来说是非常遗憾的。今年一定要完成这门课程。这门课程分为了I,II两部分。虽然在日常的工作中并不一定会使用到,但算法和数据结构的知识是可以帮助我在接触到一些技术方案时进行更深入的思考的。

  3. React 和 React Native
    这两样也是因为公司的项目需要而学习的。我在去年之前基本上就是从来没有做过客户段的内容。但这次项目中由于写代码的只有我一个人,就不得不硬着头皮上了。最开始用Android的原生开发,但始终代码写的不是很顺手,开发效率非常慢。然后尝试使用webview嵌入的方式做了一个版本,交互效果实在惨不忍睹。最后选择了React Native。虽然这个技术之前也完全没有接触过,但是在Udemy上学习了React - The Complete GuideReact Native - The Practical Guide后,我有了基本的信心,花了大概一个多月的时间把客户端重构了出来。开发效率和UI交互的效果都达到了让我基本满意的程度。

  4. Godot
    Godot是我在Zhihu上了解的一款游戏引擎,今年应该会发布4.0版本。我目前的公司的主营业务是手机游戏,我19,20年的主要工作就是游戏服务器端的开发。在B站上看了几个介绍的视频后,觉得这是一款很具有潜力的游戏引擎,当然目前它的功能和Unity以及Unreal没法比。如果未来我失去工作成为独立开发者,那么我会选择Godot来制作游戏。在Udemy上跟着几个初级的教程学习后,就用了一周的时间完成了一个2048的小游戏。今年的一个计划是使用它开发一款小游戏并上架到Google Play上。

  5. 公司的项目

2022年全年只做了一个公司的项目,这是一个由于国情不能写在简历的项目。老板看到友商赚的盆满钵满的就敲定启动这个项目。由于其特殊性,技术就只有我一个人。从21年10月,到现在做了也有一年多了,技术方案服务器更换过一次,客户端更换过两次。这个项目的前景暂时还不太明朗。

  1. 冷水浴
    在12年逛Youtube上看到一个健身up主关于冷水浴的介绍,从10月中旬到12月中旬大概坚持了2个月的冷水浴,虽然不能做到每天都洗一次冷水浴,但基本上也做的一周可以5次。从刚开始的战战兢兢到最后的坦然面对,我对我自己能做到这一点还是很满意的。但冷水浴依然挡不住奥密克戎,我不得不停止下来,直到现在我每天还都在咳嗽,看来完全恢复还需要一段时间。

2023年的展望:

  1. 读书:
    去年一年基本没有读过一本完整的书。这一点今年要做改变。至少读3本非技术类的书籍。

  2. 锻炼:
    原先上班的时间基本上没有锻炼的机会,我估摸着再不开始有规律的锻炼我这身体就废掉了。先定一个小目标吧:每周至少完成一次达到出汗级别的运动。

  3. 学习:
    3.1 在上半年通过CKS的考试。继续巩固运维方面的技能
    3.2 学习Next.js, 通过Next.js完成一个Admin site的模板
    3.3 每周完成一门Udemy的课程。

  4. 写作:
    每周至少完成一篇Blog。

  5. 工作:
    我需要换一份工作。