亚洲国产日韩欧美一区二区三区,精品亚洲国产成人av在线,国产99视频精品免视看7,99国产精品久久久久久久成人热,欧美日韩亚洲国产综合乱

Table of Contents
Preface
Special effects analysis
Load image
Rotate the picture
Particle Animation
粒子動畫分析
生產(chǎn)粒子
定義粒子
定義自定義view
添加多個粒子
粒子添加到集合中
定義動畫
更新粒子位置
修改粒子的定義
粒子運(yùn)動距離判定
粒子移動透明
定義圓形
圓形添加粒子
修改動畫
預(yù)告
Home WeChat Applet Mini Program Development Realize the irresistible special effects of NetEase Cloud Music's cosmic dust

Realize the irresistible special effects of NetEase Cloud Music's cosmic dust

Oct 13, 2020 am 11:03 AM
WeChat applet

WeChat Mini Program Development Tutorial column today teaches you how to implement the special effects of NetEase Cloud Music universe dust, step by step.

Realize the irresistible special effects of NetEase Cloud Music's cosmic dust

Preface

Some time ago, my girlfriend saw a cosmic dust special effect while using NetEase Cloud Music. She said it looked very nice and wanted me to give it to her. Open for VIP use.

Joke, as a programmer, why can’t I implement it myself! What kind of VIP is open? !

What girlfriend? Are there any programmers? I only care about the realization of special effects!

It’s 2020, and most Android developers should be experienced. If you are not proficient enough in customizing View, it will not make sense. Customizing View can be said to be a point that must be mastered in Android development, whether beginner, intermediate or advanced.

Otherwise, if the UI is accidentally designed to be too cool, wouldn’t you have to fight with him? Don’t you want to be the man in the picture below?

Realize the irresistible special effects of NetEase Cloud Music's cosmic dust

So, I don’t need to say more about the importance of customizing View. This article is for people who have basic knowledge of custom View, but are struggling with no good project imitation, or who have seen cool effects but have no ideas and don’t know how to start. Congratulations, I will take you step by step to analyze the effect, and then implement it with code.

I know you can’t deceive people without pictures. Let’s show the picture first, and let’s take a look at the final effect.

ps: In order to load faster, the gif is compressed and compressed so that everyone can have better brain clarity.

ps2: If you have a good gif compression website, can you recommend it?

Realize the irresistible special effects of NetEase Cloud Music's cosmic dust

Ahem, although the picture quality is comparable to AV picture quality, it is still It can be seen that the effect is very good. So today I will take my friends to realize this effect from beginning to end.

Realize the irresistible special effects of NetEase Cloud Music's cosmic dust

Special effects analysis

First look at the animation, we can split it into two parts to complete, one is a circular picture that is constantly rotating inside, and the other is a constantly rotating picture outside Diffusing particle animation.

We will complete it from easy to difficult, after all, you have to pick the soft persimmon.

In addition, since the focus of this article is on customizing View, the ViewGroup method is not used to achieve the combination of pictures and particle animation. Instead, separate layouts are used. The advantage of this is that you can only focus on the implementation of particle animation without having to consider measurement, layout, etc.

As for the custom ViewGroup, in the next article I will lead you to achieve a very, very, very cool effect.

Load image

Let’s observe first, first of all, this is a circular image. Secondly, it keeps spinning.

Realize the irresistible special effects of NetEase Cloud Music's cosmic dust

Ahem, don’t scold me yet, let me finish.

For circular pictures, we will use Glide to implement it. In fact, custom View implementation is also possible, but our focus is on particle effects.

First define an ImageView

<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/rootLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true">

    <ImageView
            android:id="@+id/music_avatar"        
            android:layout_centerInParent="true"
            android:layout_width="178dp"
            android:layout_height="178dp"/></RelativeLayout>復(fù)制代碼

Now we go to the Activity and use Glide to load a circular picture.

class DemoActivity : AppCompatActivity() {    private lateinit var demoBinding: ActivityDemoBinding    
    
    override fun onCreate(savedInstanceState: Bundle?) {        super.onCreate(savedInstanceState)
        demoBinding = ActivityDemoBinding.inflate(layoutInflater)
        setContentView(demoBinding.root)
        
        lifecycleScope.launch(Dispatchers.Main) {
           loadImage()
        }
    }    private suspend fun loadImage() {
        withContext(Dispatchers.IO) {
            Glide.with(this@DemoActivity)
                    .load(R.drawable.ic_music)
                    .circleCrop()
                    .into(object : ImageViewTarget<Drawable>(demoBinding.musicAvatar) {                        override fun setResource(resource: Drawable?) {     
                            demoBinding.musicAvatar.setImageDrawable(resource)
                        }
                    })
        }
    }
}復(fù)制代碼

In this way, we use Glide to load a circular image.

Rotate the picture

Now that the picture is there, the next step is to rotate it.

Then let’s start spinning.

How is rotation achieved? I don’t think I need to say more, many friends know that it is animation.

Yes, it’s animation. We use attribute animation to achieve this.

Define an attribute animation and set a click event for the picture to make it rotate

lateinit var rotateAnimator: ObjectAnimatoroverride fun onCreate(savedInstanceState: Bundle?) {
        ...
        setContentView(demoBinding.root)
        rotateAnimator = ObjectAnimator.ofFloat(demoBinding.musicAvatar, View.ROTATION, 0f, 360f)
        rotateAnimator.duration = 6000
        rotateAnimator.repeatCount = -1
        rotateAnimator.interpolator = LinearInterpolator()
        lifecycleScope.launch(Dispatchers.Main) {
            loadImage()            //添加點(diǎn)擊事件,并且啟動動畫
            demoBinding.musicAvatar.setOnClickListener {
                rotateAnimator.start()
            }
        }
}復(fù)制代碼

These are all pediatrics. I believe that the audience in front of the TV, ah no, a slip of the tongue Slip of the tongue.

I believe all of you are familiar with it, so let’s start with today’s highlight, this particle animation.

Particle Animation

Actually, when I watched particle animation a long time ago, I was also very curious about how these cool particle animations were achieved. At that time, I had no idea at all.

Especially when I see some pictures that suddenly turned into a bunch of particles, fell down, and then changed from particles to pictures again, I felt weird.

Actually, it’s not magical at all.

First we need to know what bitmap is. What is bitmap?

In mathematics, there are several concepts: point, line, and surface. Point is easy to understand, it is just a point. A line is made up of a bunch of points, and a surface is made up of a bunch of lines. Essentially, a surface is composed of countless points.

But what does this have to do with bitmap and today’s particle animation?

一個bitmap,我們可以簡單地理解為一張圖片。這個圖片是不是一個平面呢?而平面又是一堆點(diǎn)組成的,這個點(diǎn)在這里稱為像素點(diǎn)。所以bitmap就是由一堆像素點(diǎn)所組成的,有趣的是,這些像素點(diǎn)是有顏色的,當(dāng)這些像素點(diǎn)足夠小,你離得足夠遠(yuǎn)你看起來就像一幅完整的畫了。

在現(xiàn)實(shí)中也不乏這樣的例子,舉辦一些活動的時候,一個個人穿著不同顏色的衣服有序的站在廣場上,如果有一架無人機(jī)在空中看,就能看到是一幅畫。就像這樣

Realize the irresistible special effects of NetEase Cloud Music's cosmic dust

所以當(dāng)把一幅畫拆成一堆粒子的話,其實(shí)就是獲得bitmap中所有的像素點(diǎn),然后改變他們的位置就可以了。如果想要用一堆粒子拼湊出一幅畫,只需要知道這些粒子的順序,排放整齊自然就是一幅畫了。

扯遠(yuǎn)了,說這些呢其實(shí)和今天的效果沒有特別強(qiáng)的聯(lián)系,只是為了讓你能夠更好的理解粒子動畫的本質(zhì)。

Realize the irresistible special effects of NetEase Cloud Music's cosmic dust

粒子動畫分析

我們先觀察這個特效,你會發(fā)現(xiàn)有一個圓,這個圓上不斷的往外發(fā)散粒子,粒子在發(fā)散的過程中速度是不相同的。而且,在發(fā)散的過程中,透明度也在不斷變化,直到最后完全透明。

好,我們歸納一下。

  • 圓形生產(chǎn)粒子
  • 粒子速度不同,也就是隨機(jī)。
  • 粒子透明度不斷降低,直到最后消散。
  • 粒子沿著到圓心的反方向擴(kuò)散。

寫自定義View的時候千萬不要一上來就開干,而是要逐漸分析,有的時候我們遇到一個復(fù)雜的效果,更是要逐幀的分析。

而且我寫自定義View的時候有個習(xí)慣,就是一點(diǎn)點(diǎn)的實(shí)現(xiàn)效果,不會去一次性實(shí)現(xiàn)全部的效果。

所以我們第一步,生產(chǎn)粒子。

生產(chǎn)粒子

首先,我們可以知道,粒子是有顏色的,但是似乎這個效果粒子只有白色,那就指定粒子顏色為白色了。

然后我們可以得出,粒子是有位置的,位置肯定由x,y組成嘛。然后粒子還有個速度,以及透明度和半徑。

定義粒子

我們可以定義一個粒子類:

class Particle(    var x:Float,//X坐標(biāo)
    var y:Float,//Y坐標(biāo)
    var radius:Float,//半徑
    var speed:Float,//速度
    var alpha: Int//透明度)復(fù)制代碼

由于我們的這個效果看起來就像是水波一樣的漣漪,我給自定義View起名為漣漪,也就是dimple

我們來定義這個自定義View把

定義自定義view

class DimpleView(context: Context?, attrs: AttributeSet?) : View(context, attrs) {    //定義一個粒子的集合  
    private var particleList = mutableListOf<Particle>()    //定義畫筆
    var paint = Paint()
}復(fù)制代碼

一開始就直接圓形生產(chǎn)粒子著實(shí)有些難度,我先考慮考慮如何實(shí)現(xiàn)生產(chǎn)粒子把。

先不斷生產(chǎn)粒子,然后再考慮圓形的事情。

而且生產(chǎn)一堆粒子比較麻煩,我先實(shí)現(xiàn)從上到下生產(chǎn)一個粒子。

那么如何生產(chǎn)一個粒子呢?前面也說了,粒子就是個很小的點(diǎn),所以用canvas的drawCircle就可以。

那我們來吧

override fun onDraw(canvas: Canvas) {        super.onDraw(canvas)
        paint.color = Color.WHITE
        paint.isAntiAlias = true
        var particle=Particle(0f,0f,2f,2f,100)
        canvas.drawCircle(particle.x, particle.y, particle.radius, paint)
}復(fù)制代碼

畫畫嘛,就要在onDraw方法中進(jìn)行了。我們先new一個Particle,然后畫出來。

實(shí)際上這樣并沒有什么效果。為啥呢?

我們的背景是白色的,粒子默認(rèn)是白色的,你當(dāng)然看不到了。所以我們需要先做個測試,為了能看出效果。這里啊,我們把背景換成黑色。同時,為了方便測試,先把Imageview設(shè)置成不可見。然后我們看下效果

Realize the irresistible special effects of NetEase Cloud Music's cosmic dust

沒錯,就是沒什么效果。你什么都看不出來。

Realize the irresistible special effects of NetEase Cloud Music's cosmic dust

先不急,慢慢來,且聽我吹,啊不,且聽我和你慢慢道來。

我們在這里只花了一個圓,而且是在坐標(biāo)原點(diǎn)畫了一個半徑為2的點(diǎn),可以說很小很小了。自然就看不到了。

什么,你不知道原點(diǎn)在哪?

Realize the irresistible special effects of NetEase Cloud Music's cosmic dust

棕色部分就是我們的屏幕,所以原點(diǎn)就是左上角。

現(xiàn)在我們需要做的事情只有兩個,要么把點(diǎn)變大,要么改變點(diǎn)的位置。

粒子粒子的,當(dāng)然不能變大,所以我們把它放到屏幕中心去。

所以我們定義一個屏幕中心的坐標(biāo),centerX,centerY。并且在onSizeChanged方法中給它們賦值

override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {        super.onSizeChanged(w, h, oldw, oldh)
        centerX= (w/2).toFloat()
        centerY= (h/2).toFloat()
}復(fù)制代碼

那我們改一下上面的畫點(diǎn)的代碼:

override fun onDraw(canvas: Canvas) {
        ...        var particle=Particle(centerX,centerY,2f,2f,100)
        canvas.drawCircle(particle.x, particle.y, particle.radius, paint)
}復(fù)制代碼

如此,可以看到這個點(diǎn)了,雖然很小很小,但是也勝過沒有呀

Realize the irresistible special effects of NetEase Cloud Music's cosmic dust

可是這時候有人跳出來了,說你這不對啊,一個點(diǎn)有啥用?還那么小,我本來就近視,你這搞得我更看不清了。你是不是眼睛店派來的叛徒!

添加多個粒子

那好吧,我們多加幾個??墒窃撛趺醇??效果圖中是圓形的,可是我不會啊,我只能先試試一橫排添加??纯催@樣可不可以呢?我們知道,橫排的話就是y值不變,x變。好,但是為了避免我們畫出一條線,我們x值隨機(jī)增加,這樣的話看起來也比較不規(guī)則一些。

那么代碼就應(yīng)該是這樣了

override fun onDraw(canvas: Canvas) {        super.onDraw(canvas)
        paint.color = Color.WHITE
        paint.isAntiAlias = true
        for (i in 0..50){            var random= Random()            var nextX=random.nextInt((centerX*2).toInt())            var particle=Particle(nextX.toFloat(),centerY,2f,2f,100)
            canvas.drawCircle(particle.x, particle.y, particle.radius, paint)
        }

}復(fù)制代碼

由于centerX是屏幕的中心,所以它的值是屏幕寬度的一半,這里的話X的值就是在屏幕寬度內(nèi)隨機(jī)選一個值。那么效果看起來是下面這樣

Realize the irresistible special effects of NetEase Cloud Music's cosmic dust

效果看起來不錯了。

但是總有愛搞事的小伙伴又跳出來了,說你會不會寫代碼?onDraw方法一直被調(diào)用,不能定義對象你不知道么?很容易引發(fā)頻繁的GC,造成內(nèi)存抖動的。而且你這還搞個循環(huán),性能能行不?

這個小伙伴你說的非常對,是我錯了!

確實(shí),在ondraw方法中不適合定義對象,尤其是for循環(huán)中就更不能了。段時間看,我們50個粒子好像對性能的開銷不是很大,但是一旦粒子數(shù)量很多,性能開銷就會十分的大。而且,為了不掉幀,我們需要在16ms之內(nèi)完成繪制。這個不明白的話我后續(xù)會有性能優(yōu)化的專題,可以關(guān)注一下我~

這里我們測量一下50個粒子的繪制時間和5000個粒子的繪制時間。

override fun onDraw(canvas: Canvas) {        super.onDraw(canvas)
        paint.color = Color.WHITE
        paint.isAntiAlias = true
        var time= measureTimeMillis {            for (i in 0..50){                var random= Random()                var nextX=random.nextInt((centerX*2).toInt())                var particle=Particle(nextX.toFloat(),centerY,2f,2f,100)
                canvas.drawCircle(particle.x, particle.y, particle.radius, paint)
            }
        }
        Log.i("dimple","繪制時間$time ms")
}復(fù)制代碼

結(jié)果如下:50個粒子的繪制時間

Realize the irresistible special effects of NetEase Cloud Music's cosmic dust

5000個粒子的繪制時間:

Realize the irresistible special effects of NetEase Cloud Music's cosmic dust

可以看到,明顯超了16ms。所以我們需要優(yōu)化,怎么優(yōu)化?很簡單,就是不在ondraw方法中創(chuàng)建對象就好了,那我們選擇在哪里呢?

構(gòu)造方法可以嗎?好像不可以呢,這個時候還沒辦法獲得屏幕寬高,嘿嘿嘿,onSizeChanged方法就決定是你了!

粒子添加到集合中
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {        super.onSizeChanged(w, h, oldw, oldh)
        centerX= (w/2).toFloat()
        centerY= (h/2).toFloat()        val random= Random()        var nextX=0
        for (i in 0..5000){
            nextX=random.nextInt((centerX*2).toInt())
            particleList.add(Particle(nextX.toFloat(),centerY,2f,2f,100))
        }
}復(fù)制代碼

我們再來看看onDraw方法中繪制時間是多少:

override fun onDraw(canvas: Canvas) {    super.onDraw(canvas)
    paint.color = Color.WHITE
    paint.isAntiAlias = true
    var time= measureTimeMillis {
        particleList.forEach {
            canvas.drawCircle(it.x,it.y,it.radius,paint)
        }
    }
    Log.i("dimple","繪制時間$time ms")
}復(fù)制代碼

Realize the irresistible special effects of NetEase Cloud Music's cosmic dust

emmmm,好像是低于16ms了,可是這也太危險了吧,你這分分鐘就超過了16ms啊。

確實(shí)是這樣子,但是實(shí)際情況下,我們并不需要5000個這么多的粒子。又有人問,,萬一真的需要怎么辦?那就得看surfaceView了。這里就不講了

我們還是回過頭來,先把粒子數(shù)量變成50個。

現(xiàn)在粒子也有了,該實(shí)現(xiàn)動起來的效果了。

動起來,我們想想,應(yīng)該怎么做呢?效果圖是類似圓一樣的擴(kuò)散,我現(xiàn)在做不到,我往下掉這應(yīng)該不難吧?

說動就動,搞起!至于怎么動,那肯定是屬性動畫呀。

定義動畫

private var animator = ValueAnimator.ofFloat(0f, 1f)init {
        animator.duration = 2000
        animator.repeatCount = -1
        animator.interpolator = LinearInterpolator()
        animator.addUpdateListener {
            updateParticle(it.animatedValue as Float)
            invalidate()//重繪界面
        }
}復(fù)制代碼

我在這里啊,定義了一個方法updateParticle,每次動畫更新的時候啊就去更新粒子的狀態(tài)。

updateParticle方法應(yīng)該去做什么事情呢?我們來開動小腦筋想想。

如果說是粒子不斷往下掉的話,那應(yīng)該是y值不斷地增加就可以了,嗯,非常有道理。

我們來實(shí)現(xiàn)一下這個方法

更新粒子位置

private fun updateParticle(value: Float) {
    particleList.forEach {
        it.y += it.speed
    }
}override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
        ...
        animator.start()//別忘了啟動動畫
    }復(fù)制代碼

那我們現(xiàn)在來看一下效果如何

Realize the irresistible special effects of NetEase Cloud Music's cosmic dust

emmmm看起來有點(diǎn)雛形了,不過效果圖里的粒子速度似乎是隨機(jī)的,咱們這里是同步的呀。

沒關(guān)系,我們可以讓粒子的速度變成隨機(jī)的速度。我們修改添加粒子這里的代碼

override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {        super.onSizeChanged(w, h, oldw, oldh)
        centerX = (w / 2).toFloat()
        centerY = (h / 2).toFloat()        val random = Random()        var nextX = 0
        var speed=0 //定義一個速度
        for (i in 0..50) {
            nextX = random.nextInt((centerX * 2).toInt())
            speed= random.nextInt(10)+5 //速度從5-15不等
            particleList.add(
                Particle(nextX.toFloat(), centerY, 2f, speed.toFloat(), 100)
            )
        }
        animator.start()
    }復(fù)制代碼

這是效果,看起來有點(diǎn)樣子了。不過問題又來了,人家的粒子是一直散發(fā)的,你這個粒子怎么沒了就是沒了呢?Realize the irresistible special effects of NetEase Cloud Music's cosmic dust有道理,所以我覺得我們需要設(shè)置一個粒子移動的最大距離,一旦超出這個最大距離,我們啊就讓它回到初始的位置。

修改粒子的定義

class Particle(    var x:Float,//X坐標(biāo)
    var y:Float,//Y坐標(biāo)
    var radius:Float,//半徑
    var speed:Float,//速度
    var alpha: Int, //透明度
    var maxOffset:Float=300f//最大移動距離)復(fù)制代碼

如上,我們添加了一個最大移動距離。但是有時候我們往往最大移動距離都是固定的,所以我們這里給設(shè)置了一個默認(rèn)值,如果哪個粒子想特立獨(dú)行也不是不可以。

有了最大的移動距離,我們就得判定,一旦移動的距離超過了這個值,我們就讓它回到起點(diǎn)。這個判定在哪里做呢?當(dāng)然是在更新位置的地方啦

粒子運(yùn)動距離判定

private fun updateParticle(value: Float) {
        particleList.forEach {            if(it.y - centerY >it.maxOffset){
                it.y=centerY //重新設(shè)置Y值
                it.x = random.nextInt((centerX * 2).toInt()).toFloat() //隨機(jī)設(shè)置X值
                it.speed= (random.nextInt(10)+5).toFloat() //隨機(jī)設(shè)置速度
            }
            it.y += it.speed
        }
}復(fù)制代碼

本來呀,我想慢慢來,先隨機(jī)Y,在隨機(jī)X和速度。

但是我覺得可以放在一起講,因?yàn)橐粋€粒子一旦超出這個最大距離,那么它就相當(dāng)于被回收重新生成一個新的粒子了,而一個新的粒子,必然X,Y,速度都是重新生成的,這樣才能看起來效果不錯。

那我們運(yùn)行起來看看效果把。Realize the irresistible special effects of NetEase Cloud Music's cosmic dust

emmm似乎還不錯的樣子?不過人家的粒子看起來很多呀,沒關(guān)系,我們這里設(shè)置成300個粒子再試試?

Realize the irresistible special effects of NetEase Cloud Music's cosmic dust

看起來已經(jīng)不錯了。那我們接下來該怎么辦呢?是不是還有個透明度沒搞呀。

透明度的話,我們想想該如何去設(shè)置呢?首先,應(yīng)該是越遠(yuǎn)越透明,直到最大值,完全透明。這就是了,透明度和移動距離是息息相關(guān)的。

粒子移動透明

private fun updateParticle(value: Float) {
        particleList.forEach {
            ...            //設(shè)置粒子的透明度
            it.alpha= ((1f - (it.y-centerY) / it.maxOffset)  * 225f).toInt()
            ...
        }
}override fun onDraw(canvas: Canvas) {
        ...        var time = measureTimeMillis {
            particleList.forEach {                //設(shè)置畫筆的透明度
                paint.alpha=it.alpha
                canvas.drawCircle(it.x, it.y, it.radius, paint)
            }
        }
        ...
}復(fù)制代碼

再看一下效果。。。

Realize the irresistible special effects of NetEase Cloud Music's cosmic dust

看起來不錯了,有點(diǎn)意思了哦~~不過好像不夠密集,我們把粒子數(shù)量調(diào)整到500就會好很多喲。而且,不知道大家有沒有發(fā)現(xiàn)在動畫剛剛加載的時候,那個效果是很不好的。因?yàn)樗械睦悠鹗键c(diǎn)是一樣的,速度也難免會有一樣的,所以效果不是很好,只需要在添加粒子的時候,Y值也初始化即可。

override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {    super.onSizeChanged(w, h, oldw, oldh)
    ...    var nextY=0f
    for (i in 0..500) {
        ...        //初始化Y值,這里是以起始點(diǎn)作為最低值,最大距離作為最大值
        nextY= random.nextInt(400)+centerY
        speed= random.nextInt(10)+5
        particleList.add(
            Particle(nextX.toFloat(), nextY, 2f, speed.toFloat(), 100)
        )
    }
    animator.start()
}復(fù)制代碼

這樣一來,效果就會很好了,沒有一點(diǎn)問題了?,F(xiàn)在看來,似乎除了不是圓形以外,沒有什么太大的問題了。那我們下一步就該思考如何讓它變成圓形那樣生成粒子呢?

定義圓形

首先這個圓形是圓,但又不能畫出來。

什么意思?

就是說,雖然是圓形生成粒子,但是不能夠畫出來這個圓,所以這個圓只是個路徑而已。

路徑是什么?沒錯,就是Path。

熟悉的小伙伴們就知道,Path可以添加各種各樣的路徑,由圓,線,曲線等。所以我們這里就需要一個圓的路徑。

定義一個Path,添加圓。注意,我們上面講的性能優(yōu)化,不要再onDraw中定義哦。

var path = Path()override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
    ...
    path.addCircle(centerX, centerY, 280f, Path.Direction.CCW)
    ...
}復(fù)制代碼

在onSizeChanged中我們添加了一個圓,參數(shù)的意思我就不講了,小伙伴應(yīng)該都明白。

現(xiàn)在我們已經(jīng)定義了這個Path,但是我們又不畫,那我們該怎么辦呢?

我們思考一下,我們?nèi)绻胍獔A形生產(chǎn)粒子的話,是不是得需要這個圓上的任意一點(diǎn)的X,Y值有了這個X,Y值,我們才能夠?qū)⒘W拥某跏嘉恢媒o確定呢?看看有沒人有知道怎么確定位置啊,知道的小伙伴舉手示意一下

啊,等了十幾分鐘也沒見有小伙伴舉手,看來是沒人了。

Realize the irresistible special effects of NetEase Cloud Music's cosmic dust

好漢饒命!

我說,我說,其實(shí)就是PathMeasure這個類,它可以幫助我們得到在這個路徑上任意一點(diǎn)的位置和方向。不會用的小伙伴趕緊谷歌一下用法吧~或者看我代碼也很好理解的。

private val pathMeasure = PathMeasure()//路徑,用于測量擴(kuò)散圓某一處的X,Y值private var pos = FloatArray(2) //擴(kuò)散圓上某一點(diǎn)的x,yprivate val tan = FloatArray(2)//擴(kuò)散圓上某一點(diǎn)切線復(fù)制代碼

這里我們定義了三個變量,首當(dāng)其沖的就是PathMeasure類,第二個和第三個變量是一個float數(shù)組,pos是用來保存圓上某一點(diǎn)的位置信息的,其中pos[0]是X值,pos[1]是Y值。

第二個變量tan是某一點(diǎn)的切線值,你可以暫且理解為是某一點(diǎn)的角度。不過我們這個效果用不到,只是個湊參數(shù)的。

PathMeasure有個很重要的方法就是getPosTan方法。

boolean getPosTan (float distance, float[] pos, float[] tan)復(fù)制代碼

方法各個參數(shù)釋義:

參數(shù)作用備注
返回值(boolean)判斷獲取是否成功true表示成功,數(shù)據(jù)會存入 pos 和 tan 中, false 表示失敗,pos 和 tan 不會改變
distance距離 Path 起點(diǎn)的長度取值范圍: 0 <= distance <= getLength
pos該點(diǎn)的坐標(biāo)值當(dāng)前點(diǎn)在畫布上的位置,有兩個數(shù)值,分別為x,y坐標(biāo)。
tan該點(diǎn)的正切值當(dāng)前點(diǎn)在曲線上的方向,使用 Math.atan2(tan[1], tan[0]) 獲取到正切角的弧度值。

相信小伙伴還是能看明白的,我這里就不一一解釋了。

所以到了這里,我們已經(jīng)能夠獲取圓上某一點(diǎn)的位置了。還記得我們之前是怎么設(shè)置初始位置的嗎?就是Y值固定,X值隨機(jī),現(xiàn)在我們已經(jīng)能夠得到一個標(biāo)準(zhǔn)的圓的位置了。但是,很重要啊,但是如果我們按照圓的標(biāo)準(zhǔn)位置去一個個放粒子的話,豈不就是一個圓了?而我們的效果圖,位置可看起來不怎么規(guī)律。

所以我們在得到一個標(biāo)準(zhǔn)的位置之后,需要對它進(jìn)行一個隨機(jī)的偏移,偏移的也不能太大,否則成不了一個圓形。

圓形添加粒子

所以我們要修改添加粒子的代碼了。

override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {        super.onSizeChanged(w, h, oldw, oldh)
        centerX = (w / 2).toFloat()
        centerY = (h / 2).toFloat()
        path.addCircle(centerX, centerY, 280f, Path.Direction.CCW)
        pathMeasure.setPath(path, false) //添加path
        var nextX = 0f
        var speed=0
        var nextY=0f
        for (i in 0..500) {            //按比例測量路徑上每一點(diǎn)的值
            pathMeasure.getPosTan(i / 500f * pathMeasure.length, pos, tan)
            nextX = pos[0]+random.nextInt(6) - 3f //X值隨機(jī)偏移
            nextY=  pos[1]+random.nextInt(6) - 3f//Y值隨機(jī)偏移
            speed= random.nextInt(10)+5
            particleList.add(
                Particle(nextX, nextY, 2f, speed.toFloat(), 100)
            )
        }
        animator.start()
    }復(fù)制代碼

現(xiàn)在運(yùn)行起來就是這樣子了

Realize the irresistible special effects of NetEase Cloud Music's cosmic dust

咦,效果和我想象的不一樣啊。最初好像是個圓,可是不是應(yīng)該像漣漪一樣擴(kuò)散嗎,可你這還是往下落呀。

還記得我們之前定義的動畫的效果嗎,就是X值不變,Y值不斷擴(kuò)大,那可不就是一直往下落嗎?所以這里我們需要修改動畫規(guī)則。

修改動畫

問題是怎么修改動畫呢?

思考一下,效果圖中的動畫應(yīng)該是往外擴(kuò)散,擴(kuò)散是什么意思?就是沿著它到圓心的方向反向運(yùn)動,對不對?

上一張圖來理解一下

Realize the irresistible special effects of NetEase Cloud Music's cosmic dust

此時內(nèi)心圓是我們現(xiàn)在粒子所處的圓,假設(shè)有一個粒子此時在B點(diǎn),那么如果要擴(kuò)散的話,它應(yīng)該到H點(diǎn)位置。

這個H點(diǎn)的位置應(yīng)該如何獲取呢?

如果以A點(diǎn)為原點(diǎn)的話,此時B點(diǎn)的位置我們是知道的,它分別是X和Y。X=AG,Y=BG。我們也應(yīng)該能發(fā)現(xiàn),由AB延申至AH的過程中,∠Z是始終不變的。

同時我們應(yīng)該能發(fā)現(xiàn),擴(kuò)散這個過程實(shí)際上是圓變大了,所以B變挪到了H點(diǎn)上。而這個擴(kuò)大的值的意思就是圓的半徑變大了,即半徑R = AB,現(xiàn)在半徑R=AH。

AB的值我們是知道的,就是我們一開始畫的圓的半徑嘛??墒茿H是多少呢?

不妨令移動距離offset=AH-AB,那么這個運(yùn)動距離offset是多少呢?我們想一下,在之前的下落中,距離是不是等于速度乘以時間呢?而我們這里沒有時間這個變量,有的只是一次次循環(huán),循環(huán)中粒子的Y值不斷加速度。所以我們需要一個變量offset值來記錄移動的距離,

所以這個offset += speed

那我們現(xiàn)在offset知道了,也就是說AH-AB的值知道了,AB我們也知道,我們就能求出AH的值

AH=AB +offset

AH知道了,∠Z也知道了,利用三角函數(shù)我們可以得到H點(diǎn)的坐標(biāo)了。設(shè)初始半徑為R=AB

A point 為 origin,##cos(Z)=AG/ AB, sin( Z)=BG/ ABcos(∠Z)=AG/AB、sin(∠Z)=BG/ABBecauseAD

AD=AH?cos(Z)=(AH?AG)/AB=((R offset)?AG)/RAD = AH * cos(∠Z) = (AH * AG)/AB=( (R offset) * AG) / R

HD

##HD=A H?sin(Z )=(AH?B G)/AB=(( R o#ffset)?BG)/RHD = AH * sin(∠Z) = (AH*BG)/AB = ( (R offset) * BG) / R

AD=((R offset)?(B.X?centerX))/RAD = ( (R offset) * (B.X - centerX)) / R

##HD=((R offse t)?(cen terY?B.Y))/RHD = ( (R offset) * (centerY-B.Y) ) / R

H.X=AD centerX=((R offset)?(B.X?centerX))/R centerXH.X=AD centerX=( (R offset) * (B.X - centerX)) / R centerX

H.Y=centerY?HD=centerY?((R offset)?(centerY?B.Y))/RH.Y = centerY - HD = centerY -( (R offset) * (centerY-B.Y) ) / R

And this is just The upper right half, which is the first quadrant, is calculated in this way, and the calculation rules for the left half and the lower right half are also different.

Realize the irresistible special effects of NetEase Cloud Music's cosmic dust

Brother, don’t worry, listen to me first. If you still don’t understand after a while, I will personally help you kick the bench.

My friends all said that the above formula is too complicated. Each quadrant has to be calculated. Is customizing the View that complicated?

Hahahaha, actually not. Let me shake yours.

The rules are indeed as mentioned above, but who are we? Programmers, the last thing you should be afraid of is calculation. Anyway, the CPU doesn’t count for me.

We are analyzing it again, this time it must be very simple, don’t run away!

First of all, there is an angle Z. We need to record the angle of each particle, but the calculation of this angle has been explained.

We first calculate the left and right half areas. When it is in the right half area, the angle is

Z=(B.X?c enterX)/ RZ = (B.X -centerX) / R Assuming that ∠Z is 30° at this time, that means

##cosZ=0.5cos∠Z = 0.5# #c

##H.X=AH?0.5 cen terXH.X= AH * 0.5 centerX But if it is in the left half, the angle is Z

##cosZ=?0.5cos∠Z= -0.5##c

##H.X=centerX? AH?0.5H.X= centerX - AH * 0.5

H.X=cen terX AH?cosZH.X = centerX AH * cos∠Z ##H

H.Y=centerY?AH?abs(sinZ)H.Y = centerY - AH * abs( sin∠Z)

反之

H.Y=centerY+AH?abs(sinZ)H.Y = centerY + AH * abs( sin∠Z)

這樣的話隨著offset的增長,H點(diǎn)的坐標(biāo)也能夠隨時的計算出來了。

話說再多也沒用,還是代碼更為直觀。

根據(jù)上面的描述,我們需要給粒子添加兩個屬性,一個是移動距離,一個是粒子的角度

class Particle(
    ...    var offset:Int,//當(dāng)前移動距離
    var angle:Double,//粒子角度
    ...
)復(fù)制代碼

在添加粒子的地方修改:

override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
    ...    var angle=0.0
    for (i in 0..500) {
        ...        //反余弦函數(shù)可以得到角度值,是弧度
        angle=acos(((pos[0] - centerX) / 280f).toDouble())
        speed= random.nextInt(10)+5
        particleList.add(
            Particle(nextX, nextY, 2f, speed.toFloat(), 100,0,angle)
        )
    }
    animator.start()
}復(fù)制代碼

在更新粒子動畫的地方修改:

private fun updateParticle(value: Float) {
    particleList.forEach {particle->        if(particle.offset >particle.maxOffset){
            particle.offset=0
            particle.speed= (random.nextInt(10)+5).toFloat()
        }
        particle.alpha= ((1f - particle.offset / particle.maxOffset)  * 225f).toInt()
        particle.x = (centerX+ cos(particle.angle) * (280f + particle.offset)).toFloat()        if (particle.y > centerY) {
            particle.y = (sin(particle.angle) * (280f + particle.offset) + centerY).toFloat()
        } else {
            particle.y = (centerY - sin(particle.angle) * (280f + particle.offset)).toFloat()
        }
        particle.offset += particle.speed.toInt()
    }
}復(fù)制代碼

添加粒子的地方angle是角度值,用了Kotlin的acos反余弦函數(shù),這個函數(shù)返回的是0-PI的弧度制,0-PI的取值范圍也就意味著sin∠Z始終是正值,上面公式中的絕對值就不需要了。

更新的代碼很簡單,對照公式一看便知。此時我們運(yùn)行一下,效果就已經(jīng)很好了,很接近了。

Realize the irresistible special effects of NetEase Cloud Music's cosmic dust

現(xiàn)在感覺是不是好多了?就是速度有點(diǎn)快,粒子有點(diǎn)少~沒關(guān)系,我們做一些優(yōu)化工作,比如說,粒子的初始移動距離也隨機(jī)取一個值,粒子的最大距離也隨機(jī)。這樣下來,我們的效果就十分的好了

override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {    super.onSizeChanged(w, h, oldw, oldh)
    centerX = (w / 2).toFloat()
    centerY = (h / 2).toFloat()
    path.addCircle(centerX, centerY, 280f, Path.Direction.CCW)
    pathMeasure.setPath(path, false)    var nextX = 0f
    var speed=0f
    var nextY=0f
    var angle=0.0
    var offSet=0
    var maxOffset=0
    for (i in 0..2000) {
        pathMeasure.getPosTan(i / 2000f * pathMeasure.length, pos, tan)
        nextX = pos[0]+random.nextInt(6) - 3f
        nextY=  pos[1]+random.nextInt(6) - 3f
        angle=acos(((pos[0] - centerX) / 280f).toDouble())
        speed= random.nextInt(2) + 2f
        offSet = random.nextInt(200)
        maxOffset = random.nextInt(200)
        particleList.add(
            Particle(nextX, nextY, 2f, speed, 100,offSet.toFloat(),angle, maxOffset.toFloat())
        )
    }
    animator.start()
}復(fù)制代碼

其實(shí)還有很多可以優(yōu)化的地方,比如說,粒子的數(shù)量抽取為一個常量,中間圓的半徑也可以定為一個屬性值去手動設(shè)置等等。。不過這些都是小意思,相信小伙伴們一定可以自己搞定的。我就不班門弄斧了。

最后這是優(yōu)化過后的效果,接下來的與圖片結(jié)合,就希望小伙伴們自己實(shí)現(xiàn)一下啦~很簡單的??梢栽谠u論區(qū)交作業(yè)哦~

Realize the irresistible special effects of NetEase Cloud Music's cosmic dust

這是我的成品地址,做了一些額外的優(yōu)化,比如說x和y的隨機(jī)偏移等。小伙伴們可以手動去實(shí)現(xiàn)一下或者看我代碼,當(dāng)然我代碼只是為了實(shí)現(xiàn)效果,并沒有做更多的優(yōu)化,不要噴我哦:DimpleView

預(yù)告

這一次是自定義View。 下一次我將要帶大家實(shí)現(xiàn)自定義ViewGroup,效果是這樣的,是不是更加炫酷?歡迎關(guān)注我,一個喜歡自定義View和NDK開發(fā)的小喵喵~

Realize the irresistible special effects of NetEase Cloud Music's cosmic dust

Related free learning recommendations: WeChat Mini Program Development Tutorial

The above is the detailed content of Realize the irresistible special effects of NetEase Cloud Music's cosmic dust. For more information, please follow other related articles on the PHP Chinese website!

Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn

Hot AI Tools

Undress AI Tool

Undress AI Tool

Undress images for free

Undresser.AI Undress

Undresser.AI Undress

AI-powered app for creating realistic nude photos

AI Clothes Remover

AI Clothes Remover

Online AI tool for removing clothes from photos.

Clothoff.io

Clothoff.io

AI clothes remover

Video Face Swap

Video Face Swap

Swap faces in any video effortlessly with our completely free AI face swap tool!

Hot Tools

Notepad++7.3.1

Notepad++7.3.1

Easy-to-use and free code editor

SublimeText3 Chinese version

SublimeText3 Chinese version

Chinese version, very easy to use

Zend Studio 13.0.1

Zend Studio 13.0.1

Powerful PHP integrated development environment

Dreamweaver CS6

Dreamweaver CS6

Visual web development tools

SublimeText3 Mac version

SublimeText3 Mac version

God-level code editing software (SublimeText3)

Hot Topics

PHP Tutorial
1488
72
Xianyu WeChat mini program officially launched Xianyu WeChat mini program officially launched Feb 10, 2024 pm 10:39 PM

Xianyu's official WeChat mini program has quietly been launched. In the mini program, you can post private messages to communicate with buyers/sellers, view personal information and orders, search for items, etc. If you are curious about what the Xianyu WeChat mini program is called, take a look now. What is the name of the Xianyu WeChat applet? Answer: Xianyu, idle transactions, second-hand sales, valuations and recycling. 1. In the mini program, you can post idle messages, communicate with buyers/sellers via private messages, view personal information and orders, search for specified items, etc.; 2. On the mini program page, there are homepage, nearby, post idle, messages, and mine. 5 functions; 3. If you want to use it, you must activate WeChat payment before you can purchase it;

Implement image filter effects in WeChat mini programs Implement image filter effects in WeChat mini programs Nov 21, 2023 pm 06:22 PM

Implementing picture filter effects in WeChat mini programs With the popularity of social media applications, people are increasingly fond of applying filter effects to photos to enhance the artistic effect and attractiveness of the photos. Picture filter effects can also be implemented in WeChat mini programs, providing users with more interesting and creative photo editing functions. This article will introduce how to implement image filter effects in WeChat mini programs and provide specific code examples. First, we need to use the canvas component in the WeChat applet to load and edit images. The canvas component can be used on the page

Implement the drop-down menu effect in WeChat applet Implement the drop-down menu effect in WeChat applet Nov 21, 2023 pm 03:03 PM

To implement the drop-down menu effect in WeChat Mini Programs, specific code examples are required. With the popularity of mobile Internet, WeChat Mini Programs have become an important part of Internet development, and more and more people have begun to pay attention to and use WeChat Mini Programs. The development of WeChat mini programs is simpler and faster than traditional APP development, but it also requires mastering certain development skills. In the development of WeChat mini programs, drop-down menus are a common UI component, achieving a better user experience. This article will introduce in detail how to implement the drop-down menu effect in the WeChat applet and provide practical

What is the name of Xianyu WeChat applet? What is the name of Xianyu WeChat applet? Feb 27, 2024 pm 01:11 PM

The official WeChat mini program of Xianyu has been quietly launched. It provides users with a convenient platform that allows you to easily publish and trade idle items. In the mini program, you can communicate with buyers or sellers via private messages, view personal information and orders, and search for the items you want. So what exactly is Xianyu called in the WeChat mini program? This tutorial guide will introduce it to you in detail. Users who want to know, please follow this article and continue reading! What is the name of the Xianyu WeChat applet? Answer: Xianyu, idle transactions, second-hand sales, valuations and recycling. 1. In the mini program, you can post idle messages, communicate with buyers/sellers via private messages, view personal information and orders, search for specified items, etc.; 2. On the mini program page, there are homepage, nearby, post idle, messages, and mine. 5 functions; 3.

WeChat applet implements image upload function WeChat applet implements image upload function Nov 21, 2023 am 09:08 AM

WeChat applet implements picture upload function With the development of mobile Internet, WeChat applet has become an indispensable part of people's lives. WeChat mini programs not only provide a wealth of application scenarios, but also support developer-defined functions, including image upload functions. This article will introduce how to implement the image upload function in the WeChat applet and provide specific code examples. 1. Preparatory work Before starting to write code, we need to download and install the WeChat developer tools and register as a WeChat developer. At the same time, you also need to understand WeChat

Implement image rotation effect in WeChat applet Implement image rotation effect in WeChat applet Nov 21, 2023 am 08:26 AM

To implement the picture rotation effect in WeChat Mini Program, specific code examples are required. WeChat Mini Program is a lightweight application that provides users with rich functions and a good user experience. In mini programs, developers can use various components and APIs to achieve various effects. Among them, the picture rotation effect is a common animation effect that can add interest and visual effects to the mini program. To achieve image rotation effects in WeChat mini programs, you need to use the animation API provided by the mini program. The following is a specific code example that shows how to

Use WeChat applet to achieve carousel switching effect Use WeChat applet to achieve carousel switching effect Nov 21, 2023 pm 05:59 PM

Use the WeChat applet to achieve the carousel switching effect. The WeChat applet is a lightweight application that is simple and efficient to develop and use. In WeChat mini programs, it is a common requirement to achieve carousel switching effects. This article will introduce how to use the WeChat applet to achieve the carousel switching effect, and give specific code examples. First, add a carousel component to the page file of the WeChat applet. For example, you can use the &lt;swiper&gt; tag to achieve the switching effect of the carousel. In this component, you can pass b

Implement the sliding delete function in WeChat mini program Implement the sliding delete function in WeChat mini program Nov 21, 2023 pm 06:22 PM

Implementing the sliding delete function in WeChat mini programs requires specific code examples. With the popularity of WeChat mini programs, developers often encounter problems in implementing some common functions during the development process. Among them, the sliding delete function is a common and commonly used functional requirement. This article will introduce in detail how to implement the sliding delete function in the WeChat applet and give specific code examples. 1. Requirements analysis In the WeChat mini program, the implementation of the sliding deletion function involves the following points: List display: To display a list that can be slid and deleted, each list item needs to include

See all articles