首页 文章 精选 留言 我的

精选列表

搜索[svg动画],共5075篇文章
优秀的个人博客,低调大师

Android UI开发第十九篇——介绍一个开源的gifview——Android显示GIF动画

简介:android中现在没有直接显示gif的view,只能通过mediaplay来显示,且还常常不能正常显示出来,为此写了这个gifview,其用法和imageview一样 使用方法: 1-把GifView.jar加入你的项目。 2-在xml中配置GifView的基本属性,GifView继承自View类,和Button、ImageView一样是一个UI控件。如: <com.ant.liao.GifViewandroid:id="@+id/gif2" android:layout_height="wrap_content"android:layout_width="wrap_content" android:paddingTop="4px"android:paddingLeft="14px"android:enabled="false"/> 3-在代码中配置常用属性: //从xml中得到GifView的句柄 gf1=(GifView)findViewById(R.id.gif1); //设置Gif图片源 gf1.setGifImage(R.drawable.gif1); //添加监听器 gf1.setOnClickListener(this); //设置显示的大小,拉伸或者压缩 gf1.setShowDimension(300,300); //设置加载方式:先加载后显示、边加载边显示、只显示第一帧再显示 gf1.setGifImageType(GifImageType.COVER); GifView的Jar包共有四个类: GifAction.java 观察者类,监视GIF是否加载成功 GifFrame.java 里面三个成员:当前图片、延时、下张Frame的链接。 GifDecoder.java 解码线程类 GifView.java 主类,包括常用方法,如GifView构造方法、设置图片源、延迟、绘制等。 本文转自xyz_lmn51CTO博客,原文链接:http://blog.51cto.com/xyzlmn/817143,如需转载请自行联系原作者

优秀的个人博客,低调大师

每日一博 | 3000 帧动画图解 MySQL 为什么需要 binlog、redo log 和 undo log

> 全文建立在MySQL的存储引擎为InnoDB的基础上 先看一条SQL如何入库的: 这是一条很简单的更新SQL,从MySQL服务端接收到SQL到落盘,先后经过了MySQL Server层和InnoDB存储引擎。 Server层就像一个产品经理,分析客户的需求,并给出实现需求的方案。 InnoDB就像一个基层程序员,实现产品经理给出的具体方案。 在MySQL”分析需求,实现方案“的过程中,还夹杂着内存操作和磁盘操作,以及记录各种日志。 他们到底有什么用处?他们之间到底怎么配合的?MySQL又为什么要分层呢?InnoDB里面的那一块Buffer Pool又是什么? 我们慢慢分析。 分层结构 MySQL为什么要分为Server层和存储引擎两层呢? 这个问题官方也没有给出明确的答案,但是也不难猜,简单来说就是为了“解耦”。 Server层和存储引擎各司其职,分工明确,用户可以根据不同的需求去使用合适的存储引擎,多好的设计,对不对? 后来的发展也验证了“分层设计”的优越性:MySQL最初搭载的存储引擎是自研的只支持简单查询的MyISAM的前身ISAM,后来与Sleepycat合作研发了Berkeley DB引擎,支持了事务。江山代有才人出,技术后浪推前浪,MySQL在持续的升级着自己的存储引擎的过程中,遇到了横空出世的InnoDB,InnoDB的功能强大让MySQL倍感压力。 自己的存储引擎打不过InnoDB怎么办? 打不过就加入! MySQL选择了和InnoDB合作。正是因为MySQL存储引擎的插件化设计,两个公司合作的非常顺利,MySQL也在合作后不久就发布了正式支持nnoDB的4.0版本以及经典的4.1版本。 MySQL兼并天下模式也成为MySQL走向繁荣的一个重要因素。这能让MySQL长久地保持着极强竞争力。时至今日,MySQL依然占据着极高数据库市场份额,仅次于王牌数据库Oracle。 Buffer Pool 在InnoDB里,有一块非常重要的结构——Buffer Pool。 Buffer Pool是个什么东西呢? Buffer Pool就是一块用于缓存MySQL磁盘数据的内存空间。 为什么要缓存MySQL磁盘数据呢? 我们通过一个例子说明,我们先假设没有Buffer Pool,user表里面只有一条记录,记录的age = 1,假设需要执行三条SQL: 事务A:update user set age = 2 事务B:update user set age = 3 事务C:update user set age = 4 如果没有Buffer Pool,那执行就是这样的: 从图上可以看出,每次更新都需要从磁盘拿数据(1次IO),修改完了需要刷到磁盘(1次IO),也就是每次更新都需要2次磁盘IO。三次更新需要6次磁盘IO。 而有了Buffer Pool,执行就成了这样: 从图上可以看出,只需要在第一次执行的时候将数据从磁盘拿到Buffer Pool(1次IO),第三次执行完将数据刷回磁盘(1次IO),整个过程只需要2次磁盘IO,比没有Buffer Pool节省了4次磁盘IO的时间。 当然,Buffer Pool真正的运转流程没有这么简单,具体实现细节和优化技巧还有很多,由于篇幅有限,本文不做详细描述。 我想表达的是:Buffer Pool就是将磁盘IO转换成了内存操作,节省了时间,提高了效率。 Buffer Pool是提高了效率没错,但是出现了一个问题,Buffer Pool是基于内存的,而只要一断电,内存里面的数据就会全部丢失。 如果断电的时候Buffer Pool的数据还没来得及刷到磁盘,那么这些数据就丢失了吗? 还是上面的那个例子,如果三个事务执行完毕,在age = 4还没有刷到磁盘的时候,突然断电,数据就全部丢掉了: 试想一下,如果这些丢失的数据是核心的用户交易数据,那用户能接受吗? 答案是否定的。 那InnoDB是如何做到数据不会丢失的呢? 今天的第一个日志——redo log登场了。 恢复 - redo log 顾名思义,redo是重做的意思,redo log就是重做日志的意思。 redo log是如何保证数据不会丢失的呢? 就是在修改之后,先将修改后的值记录到磁盘上的redo log中,就算突然断电了,Buffer Pool中的数据全部丢失了,来电的时候也可以根据redo log恢复Buffer Pool,这样既利用到了Buffer Pool的内存高效性,也保证了数据不会丢失。 我们通过一个例子说明,我们先假设没有Buffer Pool,user表里面只有一条记录,记录的age = 1,假设需要执行一条SQL: 事务A:update user set age = 2 执行过程如下: 如上图,有了redo log之后,将age修改成2之后,马上将age = 2写到redo log里面,如果这个时候突然断电内存数据丢失,在来电的时候,可以将redo log里面的数据读出来恢复数据,用这样的方式保证了数据不会丢失。 你可能会问,redo log文件也在磁盘上,数据文件也在磁盘上,都是磁盘操作,何必多此一举?为什么不直接将修改的数据写到数据文件里面去呢? 傻瓜,因为redo log是磁盘顺序写,数据刷盘是磁盘随机写,磁盘的顺序写比随机写高效的多啊。 这种先预写日志后面再将数据刷盘的机制,有一个高大上的专业名词——WAL(Write-ahead logging),翻译成中文就是预写式日志。 虽然磁盘顺序写已经很高效了,但是和内存操作还是有一定的差距。 那么,有没有办法进一步优化一下呢? 答案是可以。那就是给redo log也加一个内存buffer,也就是redo log buffer,用这种套娃式的方法进一步提高效率。 redo log buffer具体是怎么配合刷盘呢? 在这个问题之前之前,我们先来捋一下MySQL服务端和操作系统的关系: MySQL服务端是一个进程,它运行于操作系统之上。也就是说,操作系统挂了MySQL一定挂了,但是MySQL挂了操作系统不一定挂。 所以MySQL挂了有两种情况: MySQL挂了,操作系统也挂了,也就是常说的服务器宕机了。这种情况Buffer Pool里面的数据会全部丢失,操作系统的os cache里面的数据也会丢失。 MySQL挂了,操作系统没有挂。这种情况Buffer Pool里面的数据会全部丢失,操作系统的os cache里面的数据不会丢失。 OK,了解了MySQL服务端和操作系统的关系之后,再来看redo log的落盘机制。redo log的刷盘机制由参数innodb_flush_log_at_trx_commit控制,这个参数有3个值可以设置: innodb_flush_log_at_trx_commit = 1:实时写,实时刷 innodb_flush_log_at_trx_commit = 0:延迟写,延迟刷 innodb_flush_log_at_trx_commit = 2:实时写,延迟刷 写可以理解成写到操作系统的缓存(os cache),刷可以理解成把操作系统里面的缓存刷到磁盘。 这三种策略的区别,我们分开讨论: innodb_flush_log_at_trx_commit = 1:实时写,实时刷 这种策略会在每次事务提交之前,每次都会将数据从redo log刷到磁盘中去,理论上只要磁盘不出问题,数据就不会丢失。 总结来说,这种策略效率最低,但是丢数据风险也最低。 innodb_flush_log_at_trx_commit = 0:延迟写,延迟刷 这种策略在事务提交时,只会把数据写到redo log buffer中,然后让后台线程定时去将redo log buffer里面的数据刷到磁盘。 这种策略是最高效的,但是我们都知道,定时任务是有间隙的,但是如果事务提交后,后台线程没来得及将redo log刷到磁盘,这个时候不管是MySQL进程挂了还是操作系统挂了,这一部分数据都会丢失。 总结来说这种策略效率最高,丢数据的风险也最高。 innodb_flush_log_at_trx_commit = 2:实时写,延迟刷 这种策略在事务提交之前会把redo log写到os cache中,但并不会实时地将redo log刷到磁盘,而是会每秒执行一次刷新磁盘操作。 这种情况下如果MySQL进程挂了,操作系统没挂的话,操作系统还是会将os cache刷到磁盘,数据不会丢失,如下图: 但如果MySQL所在的服务器挂掉了,也就是操作系统都挂了,那么os cache也会被清空,数据还是会丢失。如下图: 所以,这种redo log刷盘策略是上面两种策略的折中策略,效率比较高,丢失数据的风险比较低,绝大多情况下都推荐这种策略。 总结一下,redo log的作用是用于恢复数据,写redo log的过程是磁盘顺序写,有三种刷盘策略,有innodb_flush_log_at_trx_commit 参数控制,推荐设置成2。 回滚 - undo log 我们都知道,InnoDB是支持事务的,而事务是可以回滚的。 假如一个事务将age=1修改成了age=2,在事务还没有提交的时候,后台线程已经将age=2刷入了磁盘。这个时候,不管是内存还是磁盘上,age都变成了2,如果事务要回滚,找不到修改之前的age=1,无法回滚了。 那怎么办呢? 很简单,把修改之前的age=1存起来,回滚的时候根据存起来的age=1回滚就行了。 MySQL确实是这么干的!这个记录修改之前的数据的过程,叫做记录undo log。undo翻译成中文是撤销、回滚的意思,undo log的主要作用也就是回滚数据。 如何回滚呢?看下面这个图: MySQL在将age = 1修改成age = 2之前,先将age = 1存到undo log里面去,这样需要回滚的时候,可以将undo log里面的age = 1读出来回滚。 需要注意的是,undo log默认存在全局表空间里面,你可以简单的理解成undo log也是记录在一个MySQL的表里面,插入一条undo log和插入一条普通数据是类似。也就是说,写undo log的过程中同样也是要写入redo log的。 归档 - binlog undo log记录的是修改之前的数据,提供回滚的能力。 redo log记录的是修改之后的数据,提供了崩溃恢复的能力。 那binlog是干什么的呢? binlog记录的是修改之后的数据,用于归档。 和redo log日志类似,binlog也有着自己的刷盘策略,通过sync_binlog参数控制: sync_binlog = 0 :每次提交事务前将binlog写入os cache,由操作系统控制什么时候刷到磁盘 sync_binlog =1 :采用同步写磁盘的方式来写binlog,不使用os cache来写binlog sync_binlog = N :当每进行n次事务提交之后,调用一次fsync将os cache中的binlog强制刷到磁盘 那么问题来了,binlog和redo log都是记录的修改之后的值,这两者有什么区别呢?有redo log为什么还需要binlog呢? 首先看两者的一些区别: binlog是逻辑日志,记录的是对哪一个表的哪一行做了什么修改;redo log是物理日志,记录的是对哪个数据页中的哪个记录做了什么修改,如果你还不了解数据页,你可以理解成对磁盘上的哪个数据做了修改。 binlog是追加写;redo log是循环写,日志文件有固定大小,会覆盖之前的数据。 binlog是Server层的日志;redo log是InnoDB的日志。如果不使用InnoDB引擎,是没有redo log的。 但说实话,我觉得这些区别并不是redo log不能取代binlog的原因,MySQL官方完全可以调整redo log让他兼并binlog的能力,但他没有这么做,为什么呢? 我认为不用redo log取代binlog最大的原因是“没必要”。 为什么这么说呢? 第一点,binlog的生态已经建立起来。MySQL高可用主要就是依赖binlog复制,还有很多公司的数据分析系统和数据处理系统,也都是依赖的binlog。取代binlog去改变一个生态费力了不讨好。 第二点,binlog并不是MySQL的瓶颈,花时间在没有瓶颈的地方没必要。 总结 总结一下: Buffer Pool是MySQL进程管理的一块内存空间,有减少磁盘IO次数的作用。 redo log是InnoDB存储引擎的一种日志,主要作用是崩溃恢复,有三种刷盘策略,有innodb_flush_log_at_trx_commit 参数控制,推荐设置成2。 undo log是InnoDB存储引擎的一种日志,主要作用是回滚。 binlog是MySQL Server层的一种日志,主要作用是归档。 MySQL挂了有两种情况:操作系统挂了MySQL进程跟着挂了;操作系统没挂,但是MySQL进程挂了。 最后,再用一张图总结一下全文的知识点: 写在最后 这篇文章写在一年之前,本来觉得是一篇水文没想要发,最近无聊修改了一下发了出来,希望能够用动图的形式帮助到MySQL基础不太好的朋友,大神忽略就好。 需要强调的一点是,由于作者水平有限,本文只是浅显的从无到有地阐述了MySQL几种日志的大致作用,过程中省略了很多细节,比如Buffer Pool的实现细节,比如undo log和MVCC的关系,比如binlog buffer、change buffer的存在,比如redo log的两阶段提交。 如果您有任何问题,我们可以探讨,如果您在文中发现错误,还望您指出,万分感谢! 好了,今天的文章就到这里了。 感谢你的阅读!我是CoderW,我们下期再见。 > 最后,欢迎关注我的公众号“CoderW”一起探讨进步~~~~ 参考资料 《MySQL实战45讲》 《从根儿上理解MySQL》 《MySQL技术内幕—InnoDB存储引擎》第2版

优秀的个人博客,低调大师

酷炫的Android交互动画和视觉效果:高仿音悦台播放页面

新版的音悦台 APP 播放页面交互非常有意思,可以把播放器往下拖动,这个页面透明渐变,然后到底部可以左右拖动关闭播放器,然后点击视频列表有个页面弹出来的效果,十分炫酷,于是我自己动手实现了这个交互炫酷的播放器页面。 1.废话不多说,直接演示实现效果 1.1.点击某个视频,然后手指上下拖动,播放器做尺寸比例的渐变,视频相关信息做透明度渐变 1.2.播放器只有在底部的时候,才能左右拖动,此时播放器做透明度渐变,拖动一定范围可以关闭播放器;然后它只有在原始位置的一小段距离内可以往上拖动 1.3.点击视频列表的时候,若是上次视频是左右拖动关闭的话,会有个弹起播放页面的效果;若是返回键和返回箭头则无效果 2.实现的思路讲解 毫无疑问,需要自定义一个容器,然后处理它的触摸事件,对它的子 View 进行不同的处理。触摸事件的处理使用 ViewDragHelper 是再适合不过了,然后你需要实现容器 onMeasure 和 onLayout,由于使用了 ViewDragHelper,有些坑在代码解析的时候就会讲解。 播放页面是用新的 Activity 还仅仅是当前 Activity 的View的问题,由于播放器缩小到底部的时候,用户是可以滑动视频列表的,所以我个人认为就是在当前 Activity 放置一个自定义容器即可,因此为了效率考虑你可以用 ViewStub 来懒加载处理,这里方便演示我就直接 View 的形式了。 3.代码解析 3.1.需要的变量 3.2.初始化做 ViewDragHelper 的初始化,然后 post 拿到两个子 View,这里强制规定只能有两个子元素 3.3. ViewDragHelper 的回调需要做的事情比较多,在 mFlexView 拖动的时候需要同时设置 mFlexView 和 mFollowView 的相应变化效果,在 mFlexView 释放的时候需要处理关闭或收起等效果 3.4.接下来是处理测量和定位,我们实现的排列效果类似 LinearLayout 垂直排列的效果,这里被 measureChildWithMargins 的 heightUse 摆了一道;onLayout 的时候在位置缓存不为空的时候直接定位是因为 ViewDragHelper 在处理触摸事件子元素在做一些平移之类的,若是有元素更新了 UI 会导致重新 Layout,例如我的播放器在更新时间的 TextView 时就会如此,因此在 FlexCallback 的 onViewPositionChanged 方法记录位置,在重新 Layout 时恢复位置即可,这个也坑了好久 3.5.触摸事件的处理,由于缩放不会影响 mFlexView 真实宽高,ViewDragHelper 仍然会阻断 mFlexView 的真实宽高的区域,所以这里判断手指是否落在 mFlexView 视觉上的范围内,在才去调 ViewDragHelper 的 shouldInterceptTouchEvent 方法 3.6.在 computeScroll 中,若是 mIsClosing 为 true,即关闭的整个平移执行完毕了,通知回调事件 3.7.容器实现了,接下来我们继承 YytLayout 实现播放器页面的组合控件即可,再封装一些常用的方法,这里使用的是大名鼎鼎的 Ijkplayer 实现的播放器,屏蔽了 IjkVideoView 的触摸事件自己处理了;顺带一提,为了实现播放器 Controller 跟随拖动缩放的效果,放弃了常用的 PopupWindow 实现的思路,IjkController 直接是添加到 IjkVideoView 中的,要不弹窗实现跟随播放器太麻烦了 /** *CreatedbyOubowuon2016/12/2717:32.<p> *仿音悦台播放页面的具体实现,组合控件的形式 */ publicclassYytPlayerextendsYytLayout{ privateIjkControllermIjkController; privateIjkVideoViewmIjkVideoView; privateImageViewmIvAvatar; privateTextViewmTvName; privateTextViewmTvTime; privateTextViewmTvTitle; privateTextViewmTvDesc; privateRecyclerViewmYytRecyclerView; privateVideoListAdaptermVideoListAdapter; publicYytPlayer(Contextcontext,AttributeSetattrs){ super(context,attrs); init(context,attrs); } privatevoidinit(Contextcontext,AttributeSetattrs){ //继承YytLayout并且通过merge标签减少层级来实现组合控件 LayoutInflater.from(context).inflate(R.layout.yyt_player,this,true); setOnLayoutStateListener(newOnLayoutStateListener(){ @Override publicvoidonClose(){ setVisibility(View.INVISIBLE); mIjkVideoView.release(true); } }); mIjkVideoView=(IjkVideoView)findViewById(R.id.ijk_player_view); finalintscaledTouchSlop=ViewConfiguration.get(getContext()).getScaledTouchSlop(); mIjkVideoView.setOnTouchListener(newOnTouchListener(){ floatmDownX=0; floatmDownY=0; booleanmClickCancel; @Override publicbooleanonTouch(Viewv,MotionEventevent){ floatx=event.getX(); floaty=event.getY(); switch(event.getAction()){ caseMotionEvent.ACTION_DOWN: mDownX=x; mDownY=y; break; caseMotionEvent.ACTION_MOVE: if(Math.abs(mDownX-x)>scaledTouchSlop||Math.abs(mDownY-y)>scaledTouchSlop){ mClickCancel=true; } break; caseMotionEvent.ACTION_UP: if(!mClickCancel&&Math.abs(mDownX-x)<=scaledTouchSlop&&Math.abs(mDownY-y)<=scaledTouchSlop){ //点击事件偶尔失效,只好这里自己解决了 if(isHorizontalDragEnable()){ expand(); }else{ mIjkVideoView.toggleMediaControlsVisibility(); } } mClickCancel=false; break; caseMotionEvent.ACTION_CANCEL: mClickCancel=false; break; } returntrue; } }); mIvAvatar=(ImageView)findViewById(R.id.iv_avatar); mTvName=(TextView)findViewById(R.id.tv_name); mTvTime=(TextView)findViewById(R.id.tv_time); mTvTitle=(TextView)findViewById(R.id.tv_title); mTvDesc=(TextView)findViewById(R.id.tv_desc); mVideoListAdapter=newVideoListAdapter(); mVideoListAdapter.setOnItemClickCallback(newOnItemClickCallback(){ @Override publicvoidonClick(Viewview,intposition){ intpos=(Integer)view.getTag(); VideoSummarysummary=mVideoListAdapter.getData().get(pos); playVideo(mVideoListAdapter.getData(),summary); } }); mYytRecyclerView=(RecyclerView)findViewById(R.id.yyt_recycler_view); GridLayoutManagergridLayoutManager=newGridLayoutManager(context,2,LinearLayoutManager.VERTICAL,false); mYytRecyclerView.setLayoutManager(gridLayoutManager); mYytRecyclerView.setNestedScrollingEnabled(false); mYytRecyclerView.addItemDecoration(newVideoListItemDecoration(context)); mYytRecyclerView.setAdapter(mVideoListAdapter); } //播放视频 privatevoidplayVideo(Stringpath,Stringname){ try{ if(mIjkController==null){ IjkMediaPlayer.loadLibrariesOnce(null); IjkMediaPlayer.native_profileBegin("libijkplayer.so"); mIjkController=newIjkController(mIjkVideoView,name); mIjkController.setOnViewStateListener(newIjkController.OnViewStateListener(){ @Override publicvoidonBackPress(){ stop(); } }); mIjkVideoView.setMediaController(mIjkController); mIjkVideoView.setOnPreparedListener(newIMediaPlayer.OnPreparedListener(){ @Override publicvoidonPrepared(IMediaPlayermp){ mIjkVideoView.start(); } }); mIjkVideoView.setOnErrorListener(newIMediaPlayer.OnErrorListener(){ @Override publicbooleanonError(IMediaPlayermp,intwhat,intextra){ Toast.makeText(getContext(),"视频播放出错了╮(╯Д╰)╭",Toast.LENGTH_SHORT).show(); returntrue; } }); }else{ //重新设置视频名字 mIjkController.setVideoName(name); } //设置这个TextureView播放器缩放就正常了 mIjkVideoView.setRender(IjkVideoView.RENDER_TEXTURE_VIEW); //因为每次setRender都会移除view再添加,为了缩放效果这里控制器是添加到IjkVideoView中的,所以这里也要重新添加才能在IjkVideoView的最上面 mIjkController.updateControlView(); //显示加载条 mIjkController.showProgress(); //播放视频 mIjkVideoView.setVideoURI(Uri.parse(path)); }catch(UnsatisfiedLinkErrore){ e.printStackTrace(); Toast.makeText(getContext(),"你的CPU是"+Build.CPU_ABI+",当前播放器使用的编译版本"+BuildConfig.FLAVOR+"不匹配!",Toast.LENGTH_LONG).show(); } } /** *显示布局,并且播放视频 * *@paramdata视频列表,用于播放页面下面的列表布局 *@paramsummary播放的视频信息 */ publicvoidplayVideo(List<VideoSummary>data,VideoSummarysummary){ //拿到数据,设置到播放的布局的相关信息 Glide.with(getContext()).load(summary.mTopicImg).transform(newGlideCircleTransform(getContext())).into(mIvAvatar); mTvName.setText(summary.mTopicName); mTvTime.setText(summary.mPtime); mTvTitle.setText(Html.fromHtml(summary.mTitle)); if(summary.mDescription.isEmpty()){ mTvDesc.setText(summary.mTopicDesc); }else{ mTvDesc.setText(Html.fromHtml(summary.mDescription)); } //设置YytLayout可见,并且展开 setVisibility(View.VISIBLE); expand(); mVideoListAdapter.setData(data); mVideoListAdapter.setItemWidth(mYytRecyclerView.getWidth()/2); mVideoListAdapter.notifyDataSetChanged(); //播放视频 playVideo(summary.mMp4HdUrl==null?summary.mMp4Url:summary.mMp4HdUrl,summary.mTitle); } //开始播放 publicvoidstart(){ if(mIjkVideoView!=null&&!mIjkVideoView.isPlaying()){ mIjkVideoView.start(); } } //暂停播放 publicvoidpause(){ if(mIjkVideoView!=null&&mIjkVideoView.isPlaying()){ mIjkVideoView.pause(); } } //停止播放 publicvoidstop(){ setVisibility(View.INVISIBLE); if(mIjkVideoView!=null){ mIjkVideoView.release(true); } } publicbooleanisShowing(){ returngetVisibility()==VISIBLE; } } 4.总结 说难也不难,就是各种抠细节需要脑洞,各位不妨看到好玩的交互自己打开脑洞一下,接下来可能要实现下 UC 浏览器播放器的效果,感觉也是非常有意思。 作者:oubowu 来源:51CTO

优秀的个人博客,低调大师

iPhone 8启动Face ID解锁 ,还玩起了3D动画表情

随着苹果2017年秋季发布会的临近,关于新机 iPhone 8 的爆料和泄密也越来越多。此前由于各种 HomePod 固件消息的泄露,大家对新机的外观、特性等多多少少都有了了解。但是,近两日外媒又泄露了iOS 11系统最终版本的新料,让大家对这台尚未面世的新机又有了更细节的了解。 雷锋网9月9日消息,据外媒 9to5mac 8日报道,新机 iPhone 8 将以 Face ID 取代 Touch ID。同时,iOS 11也将加入“Animoji”功能,用户可通过自己的面部表情来创建自定义3D版本的表情。此外,AirPods也将迎来一次升级。 Face ID解锁 图片来源:The Verge 这一新功能被加入到新机的硬件当中,以取代此前版本的 Touch ID。目前来看,这是用户使用脸部解锁iPhone 8 的新方式的官方名称。 据 9to5

资源下载

更多资源
Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

腾讯云软件源

腾讯云软件源

为解决软件依赖安装时官方源访问速度慢的问题,腾讯云为一些软件搭建了缓存服务。您可以通过使用腾讯云软件源站来提升依赖包的安装速度。为了方便用户自由搭建服务架构,目前腾讯云软件源站支持公网访问和内网访问。

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。

WebStorm

WebStorm

WebStorm 是jetbrains公司旗下一款JavaScript 开发工具。目前已经被广大中国JS开发者誉为“Web前端开发神器”、“最强大的HTML5编辑器”、“最智能的JavaScript IDE”等。与IntelliJ IDEA同源,继承了IntelliJ IDEA强大的JS部分的功能。

用户登录
用户注册