起因
前些日子在网上看到了一个h5的比拟炫的3D球体文字效果,感觉挺有意思,就筹备在Android侧进行一下复现,废话少说,先看一下成果(gif看上去有些卡,理论不会)
外围原理
文字坐标
首先要做的就是为每个文字确定一个坐标,Android采纳的是左手坐标系,而且咱们的成果又是一个球体,所以我采纳了球面坐标系计算每个文字的坐标。
y = radius * cos(Math.toRadians(this.upDegree))z = -radius * sin(Math.toRadians(this.upDegree)) * sin(Math.toRadians(this.bottomDegree))x = radius * sin(Math.toRadians(this.upDegree)) * cos(Math.toRadians(this.bottomDegree))
其中radius为圆心到球面的连线长度,也就是球体的半径,upDegree为连线与y轴正方向的夹角,范畴为[0,180],bottomDegree为连线在xz轴确定的立体上的投影与x轴正方向的夹角,范畴为[0,360].
文字色彩与大小
当文字转到与x轴正方向夹角为90度的时候,此时文字最大,色彩最深,270度时最小,色彩最浅,270度到360度则是上述过程的逆过程。为此咱们定义一个变量factor用于形容文字色彩和大小的扭转水平,范畴为【minFactor,1】minFactor能够通过内部变量传入。
依据后面的形容,咱们能够确定factor的函数为
factor = minFactor.coerceAtLeast( when (bottomDegree) { in 0.0..90.0 -> { 1.0 / Math.PI * Math.toRadians(bottomDegree) + 0.5 } in 270.0..360.0 -> { 1.0 / Math.PI * Math.toRadians(bottomDegree) - 1.5 } else -> { -1.0 / Math.PI * Math.toRadians(bottomDegree) + 1.5 } } )
通过在不同的角度我构建了三个分段的线性函数来示意。
计算文字坐标
定义类WordItem用以示意每个文字,坐标以及其对应的factor,在onMeasure的时候为所有文字计算相应的坐标,并存储在wordItemList成员变量中。
class WordItem( var text: String, var upDegree: Double = 0.0, var bottomDegree: Double = 0.0, var x: Double = 0.0, var y: Double = 0.0, var z: Double = 0.0, var factor: Double = 0.0) { fun cal(radius: Double, upDegree: Double, bottomDegree: Double, minFactor: Double) { this.upDegree = upDegree % 180 this.bottomDegree = bottomDegree % 360 y = radius * cos(Math.toRadians(this.upDegree)) z = -radius * sin(Math.toRadians(this.upDegree)) * sin(Math.toRadians(this.bottomDegree)) x = radius * sin(Math.toRadians(this.upDegree)) * cos(Math.toRadians(this.bottomDegree)) factor = minFactor.coerceAtLeast( when (bottomDegree) { in 0.0..90.0 -> { 1.0 / Math.PI * Math.toRadians(bottomDegree) + 0.5 } in 270.0..360.0 -> { 1.0 / Math.PI * Math.toRadians(bottomDegree) - 1.5 } else -> { -1.0 / Math.PI * Math.toRadians(bottomDegree) + 1.5 } } ) } fun move(radius: Double, upOffset: Double, bottomOffset: Double, minFactor: Double) { cal(radius, upDegree + upOffset, bottomDegree + bottomOffset, minFactor) }}
private fun genWordItemList(): MutableList<WordItem>? { wordList?.let { list -> val wordItemList = mutableListOf<WordItem>() var upDegree = 0.0 for (row in 0 until circleRowNum) { upDegree += upDegreeGap upDegree %= 180.0 var bottomDegree = 0.0 for (col in 0 until perNumInCircle) { val index = row * perNumInCircle + col if (index < wordList?.size ?: 0) { bottomDegree += bottomDegreeGap bottomDegree %= 360.0 val wordItem = WordItem(list[index]) wordItem.cal(radius, upDegree, bottomDegree, minFactor) wordItemList.add(wordItem) } } } return wordItemList } return null }
绘制文字
首先依据factor设置画笔文字的大小以及相应的alpha值,而后在依据文字大小计算其相应的地位,进行绘制,并且一直减少bottomDegreeOffset,批改每个文字的坐标,实现旋转。
canvas?.let { canvas -> wordItemList?.forEach { wordItem -> wordItem.move(radius, 0.0, 1.0, minFactor) paint.textSize = (wordItem.factor * maxTextSize).toFloat() paint.alpha = 30.coerceAtLeast((wordItem.factor * 255).toInt()) textRect.setEmpty() paint.getTextBounds(wordItem.text, 0, wordItem.text.length, textRect) canvas.drawText( wordItem.text, ((width - paddingLeft - paddingRight) / 2 + wordItem.x - textRect.width() / 2).toFloat(), ((height - paddingTop - paddingBottom) / 2 + wordItem.y - textRect.height() / 2).toFloat(), paint ) } postInvalidate() }
Android高级开发零碎进阶笔记、最新面试温习笔记PDF,我的GitHub
文末
您的点赞珍藏就是对我最大的激励!
欢送关注我,分享Android干货,交换Android技术。
对文章有何见解,或者有何技术问题,欢送在评论区一起留言探讨!