整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

程序员“520”专属表达方式

者:huachao1001

博客:https://juejin.im/entry/576b6430816dfa0055bdaf71

码仔,你知道今天是什么日子吗?

嘿嘿~当然知道!5.2.0~

哟~ 这也知道!

那是~~还能给你show一段!

看好了!

几年前,看到过有个牛人用HTML5绘制了浪漫的爱心表白动画(HTML5的链接请查看原文~),发现原来程序员也是可以很浪……漫…..的……

那么在Android怎么打造如此这个效果呢?参考了一下前面HTML5的算法,在Android中实现了类似的效果。(似不似很腻害)

先贴上最终效果图:

生成心形线

心形线的表达式可以参考:桃心线。里面对桃心线的表达式解析的挺好。可以通过使用极坐标的方式,传入角度和距离(常量)计算出对应的坐标点。其中距离是常量值,不需改变,变化的是角度。

桃心线极坐标方程式为:

x = 16×sin3αy = 13×cosα − 5×cos2α − 2×cos3α − cos4α

如果生成的桃心线不够大,可以吧x、y乘以一个常数,使之变大。考虑到大部分人都不愿去研究具体的数学问题,我们直接把前面HTML5的JS代码直接翻译成Java代码就好。代码如下:

public Point getHeartPoint(float angle) {
float t = (float) (angle / Math.PI);
float x = (float) (19.5 * (16 * Math.pow(Math.sin(t), 3)));
float y = (float) (-20 * (13 * Math.cos(t) - 5 * Math.cos(2 * t) - 2 * Math.cos(3 * t) - Math.cos(4 * t)));
return new Point(offsetX + (int) x, offsetY + (int) y);
}

其中offsetX和offsetY是偏移量。使用偏移量主要是为了能让心形线处于中央。offsetX和offsetY的值分别为:

offsetX = width / 2;
offsetY = height / 2 - 55;

通过这个函数,我们可以将角度从(0,180)变化,不断取点并画点将这个心形线显示出来。好了,我们自定义一个View,然后把这个心形线画出来吧!

@Override
protected void onDraw(Canvas canvas) {
float angle = 10;
while (angle < 180) {
Point p = getHeartPoint(angle);
canvas.drawPoint(p.x, p.y, paint);
angle = angle + 0.02f;
}
}

运行结果如下:

绘制花瓣原理

我们想要的并不是简单绘制一个桃心线,要的是将花朵在桃心线上摆放。

首先,得要知道怎么绘制花朵,而花朵是由一个个花瓣组成。因此绘制花朵的核心是绘制花瓣。

绘制花瓣的原理是:3次贝塞尔曲线。三次贝塞尔曲线是由两个端点和两个控制点决定。

假设花芯是一个圆,有n个花瓣,那么两个端点与花芯的圆心连线之间的夹角即为360/n。因此可以根据花瓣数量和花芯半径确定每个花瓣的位置。将两个端点与花芯的圆心连线的延长线分别确定另外两个控制点。通过随机生成花芯半径、每个花瓣的起始角以及随机确定延长线得到两个控制点,可以绘制一个随机的花朵。

参数的改变如下图所示:

将花朵绘制到桃心线上

一大波代码来袭

首先定义花瓣类Petal:

package com.hc.testheart;

import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;

/**
* Package com.example.administrator.testrecyclerview
* Created by HuaChao on 2016/5/25.
*/
public class Petal {
private float stretchA;//第一个控制点延长线倍数
private float stretchB;//第二个控制点延长线倍数
private float startAngle;//起始旋转角,用于确定第一个端点
private float angle;//两条线之间夹角,由起始旋转角和夹角可以确定第二个端点
private int radius = 2;//花芯的半径
private float growFactor;//增长因子,花瓣是有开放的动画效果,这个参数决定花瓣展开速度
private int color;//花瓣颜色
private boolean isFinished = false;//花瓣是否绽放完成
private Path path = new Path;//用于保存三次贝塞尔曲线
private Paint paint = new Paint;//画笔
//构造函数,由花朵类调用
public Petal(float stretchA, float stretchB, float startAngle, float angle, int color, float growFactor) {
this.stretchA = stretchA;
this.stretchB = stretchB;
this.startAngle = startAngle;
this.angle = angle;
this.color = color;
this.growFactor = growFactor;
paint.setColor(color);
}
//用于渲染花瓣,通过不断更改半径使得花瓣越来越大
public void render(Point p, int radius, Canvas canvas) {
if (this.radius <= radius) {
this.radius += growFactor; // / 10;
} else {
isFinished = true;
}
this.draw(p, canvas);
}

//绘制花瓣,参数p是花芯的圆心的坐标
private void draw(Point p, Canvas canvas) {
if (!isFinished) {
path = new Path;
//将向量(0,radius)旋转起始角度,第一个控制点根据这个旋转后的向量计算
Point t = new Point(0, this.radius).rotate(MyUtil.degrad(this.startAngle));
//第一个端点,为了保证圆心不会随着radius增大而变大这里固定为3
Point v1 = new Point(0, 3).rotate(MyUtil.degrad(this.startAngle));
//第二个端点
Point v2 = t.clone.rotate(MyUtil.degrad(this.angle));
//延长线,分别确定两个控制点
Point v3 = t.clone.mult(this.stretchA);
Point v4 = v2.clone.mult(this.stretchB);
//由于圆心在p点,因此,每个点要加圆心坐标点
v1.add(p);
v2.add(p);
v3.add(p);
v4.add(p);
path.moveTo(v1.x, v1.y);
//参数分别是:第一个控制点,第二个控制点,终点
path.cubicTo(v3.x, v3.y, v4.x, v4.y, v2.x, v2.y);
}
canvas.drawPath(path, paint);
}
}

花瓣类是最重要的类,因为真正绘制在屏幕上的是一个个小花瓣。每个花朵包含一系列花瓣,花朵类Bloom如下:

package com.hc.testheart;
import android.graphics.Canvas;
import java.util.ArrayList;

/**
* Package com.example.administrator.testrecyclerview
* Created by HuaChao on 2016/5/25.
*/
public class Bloom {
private int color;
private Point point;
private int radius;
private ArrayList petals;

public Point getPoint {
return point;
}

public Bloom(Point point, int radius, int color, int petalCount) {
this.point = point;
this.radius = radius;
this.color = color;
petals = new ArrayList<>(petalCount);

float angle = 360f / petalCount;
int startAngle = MyUtil.randomInt(0, 90);
for (int i = 0; i < petalCount; i++) {
float stretchA = MyUtil.random(Garden.Options.minPetalStretch, Garden.Options.maxPetalStretch);
float stretchB = MyUtil.random(Garden.Options.minPetalStretch, Garden.Options.maxPetalStretch);
int beginAngle = startAngle + (int) (i * angle);
float growFactor = MyUtil.random(Garden.Options.minGrowFactor, Garden.Options.maxGrowFactor);
this.petals.add(new Petal(stretchA, stretchB, beginAngle, angle, color, growFactor));
}
}

public void draw(Canvas canvas) {
Petal p;
for (int i = 0; i < this.petals.size; i++) {
p = petals.get(i);
p.render(point, this.radius, canvas);
}
}

public int getColor {
return color;
}
}

接下来是花园类Garden,主要用于创建花朵以及一些相关配置:

package com.hc.testheart;
import java.util.ArrayList;

/**
* Package com.example.administrator.testrecyclerview
* Created by HuaChao on 2016/5/24.
*/
public class Garden {
public Bloom createRandomBloom(int x, int y) {
int radius = MyUtil.randomInt(Options.minBloomRadius, Options.maxBloomRadius);
int color = MyUtil.randomrgba(Options.minRedColor, Options.maxRedColor, Options.minGreenColor, Options.maxGreenColor, Options.minBlueColor, Options.maxBlueColor, Options.opacity);
int petalCount = MyUtil.randomInt(Options.minPetalCount, Options.maxPetalCount);
return createBloom(x, y, radius, color, petalCount);
}

public Bloom createBloom(int x, int y, int radius, int color, int petalCount) {
return new Bloom(new Point(x, y), radius, color, petalCount);
}

static class Options {
public static int minPetalCount = 8;
public static int maxPetalCount = 15;
public static float minPetalStretch = 2f;
public static float maxPetalStretch = 3.5f;

public static float minGrowFactor = 1f;
public static float maxGrowFactor = 1.1f;

public static int minBloomRadius = 8;
public static int maxBloomRadius = 10;

public static int minRedColor = 128;
public static int maxRedColor = 255;
public static int minGreenColor = 0;
public static int maxGreenColor = 128;
public static int minBlueColor = 0;
public static int maxBlueColor = 128;

public static int opacity = 50;
}
}

考虑到刷新的比较频繁,选择使用SurfaceView作为显示视图。

自定义一个HeartView继承SurfaceView。

代码如下:

package com.hc.testheart;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

import java.util.ArrayList;

/**
* Package com.hc.testheart
* Created by HuaChao on 2016/5/25.
*/
public class HeartView extends SurfaceView implements SurfaceHolder.Callback {
SurfaceHolder surfaceHolder;
int offsetX;
int offsetY;
private Garden garden;
private int width;
private int height;
private Paint backgroundPaint;
private boolean isDrawing = false;
private Bitmap bm;
private Canvas canvas;
private int heartRadio = 1;

public HeartView(Context context) {
super(context);
init;
}

public HeartView(Context context, AttributeSet attrs) {
super(context, attrs);
init;
}

private void init {
surfaceHolder = getHolder;
surfaceHolder.addCallback(this);
garden = new Garden;
backgroundPaint = new Paint;
backgroundPaint.setColor(Color.rgb(0xff, 0xff, 0xe0));
}

ArrayList blooms = new ArrayList<>;

public Point getHeartPoint(float angle) {
float t = (float) (angle / Math.PI);
float x = (float) (heartRadio * (16 * Math.pow(Math.sin(t), 3)));
float y = (float) (-heartRadio * (13 * Math.cos(t) - 5 * Math.cos(2 * t) - 2 * Math.cos(3 * t) - Math.cos(4 * t)));
return new Point(offsetX + (int) x, offsetY + (int) y);
}

private void drawHeart {
canvas.drawRect(0, 0, width, height, backgroundPaint);
for (Bloom b : blooms) {
b.draw(canvas);
} Canvas c = surfaceHolder.lockCanvas;
c.drawBitmap(bm, 0, 0, );
surfaceHolder.unlockCanvasAndPost(c);
}

public void reDraw {
blooms.clear;
drawOnNewThread;
}

@Override
public void draw(Canvas canvas) {
super.draw(canvas);
}

private void drawOnNewThread {
new Thread {
@Override
public void run {
if (isDrawing) return;
isDrawing = true;
float angle = 10;
while (true) {
Bloom bloom = getBloom(angle);
if (bloom != ) {
blooms.add(bloom);
}
if (angle >= 30) {
break;
} else {
angle += 0.2;
}
drawHeart;
try {
sleep(20);
} catch (InterruptedException e) {
e.printStackTrace;
}
}
isDrawing = false;
}
}.start;
}

private Bloom getBloom(float angle) {
Point p = getHeartPoint(angle);
boolean draw = true;
/**循环比较新的坐标位置是否可以创建花朵,
* 为了防止花朵太密集
* */
for (int i = 0; i < blooms.size; i++) {
Bloom b = blooms.get(i);
Point bp = b.getPoint;
float distance = (float) Math.sqrt(Math.pow(p.x - bp.x, 2) + Math.pow(p.y - bp.y, 2));
if (distance < Garden.Options.maxBloomRadius * 1.5) {
draw = false;
break;
}
}

if (draw) {
Bloom bloom = garden.createRandomBloom(p.x, p.y);
return bloom;
}
return ;
}

@Override
public void surfaceCreated(SurfaceHolder holder) {
}

@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
this.width = width;
this.height = height;
heartRadio = width * 30 / 1080;

offsetX = width / 2;
offsetY = height / 2 - 55;
bm = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);
canvas = new Canvas(bm);
drawOnNewThread;
}

@Override
public void surfaceDestroyed(SurfaceHolder holder) {
}
}

还有两个比较重要的工具类

Point.java保存点信息,或者说是向量信息。包含向量的基本运算。

package com.hc.testheart;

/**
* Package com.hc.testheart
* Created by HuaChao on 2016/5/25.
*/
public class Point {
public int x;
public int y;

public Point(int x, int y) {
this.x = x;
this.y = y;
}

public Point rotate(float theta) {
int x = this.x;
int y = this.y;
this.x = (int) (Math.cos(theta) * x - Math.sin(theta) * y);
this.y = (int) (Math.sin(theta) * x + Math.cos(theta) * y);
return this;
}

public Point mult(float f) {
this.x *= f;
this.y *= f;
return this;
}

public Point clone {
return new Point(this.x, this.y);
}

public float length {
return (float) Math.sqrt(this.x * this.x + this.y * this.y);
}

public Point subtract(Point p) {
this.x -= p.x;
this.y -= p.y;
return this;
}

public Point add(Point p) {
this.x += p.x;
this.y += p.y;
return this;
}

public Point set(int x, int y) {
this.x = x;
this.y = y;
return this;
}
}

工具类MyUtil.java主要是产生随机数、颜色等

package com.hc.testheart;

import android.graphics.Color;

/**
* Package com.example.administrator.testrecyclerview
* Created by HuaChao on 2016/5/25.
*/
public class MyUtil {
public static float circle = (float) (2 * Math.PI);
public static int rgba(int r, int g, int b, int a) {
return Color.argb(a, r, g, b);
}

public static int randomInt(int min, int max) {
return (int) Math.floor(Math.random * (max - min + 1)) + min;
}

public static float random(float min, float max) {
return (float) (Math.random * (max - min) + min);
}

//产生随机的argb颜色
public static int randomrgba(int rmin, int rmax, int gmin, int gmax, int bmin, int bmax, int a) {
int r = Math.round(random(rmin, rmax));
int g = Math.round(random(gmin, gmax));
int b = Math.round(random(bmin, bmax));
int limit = 5;
if (Math.abs(r - g) <= limit && Math.abs(g - b) <= limit && Math.abs(b - r) <= limit) {
return rgba(rmin, rmax, gmin, gmax);
} else {
return rgba(r, g, b, a);
}
}

//角度转弧度
public static float degrad(float angle) {
return circle / 360 * angle;
}
}

好了,目前为止,就可以得到上面的效果了。

码仔只能帮你到这里了!

(源码地址请查看原文~)

近期文章:

  • 送三个福利(专属)

  • 今天我把APP的编译速度缩短了近5倍

  • 从XML变成View,它经历了什么?

今日问题:

你有没有写过浪漫的程序?

快来码仔社群解锁新姿势吧!社群升级:Max你的学习效率

,就关注我。

人狠,话不多,直接进入主题;

C程序员

简单粗暴“我爱你”

#include <stdio.h>
#include <stdlib.h>

int main()
{
    int i,k;
    printf("我我我我我我  爱爱爱爱爱爱  你你你你你你你\n");
    for(i=0;i<3;i++)
    printf("我                      爱  你          你\n");
    printf("我我我我我我  爱爱爱爱爱爱  你          你\n");
    for(k=0;k<3;k++)
    printf("          我  爱            你          你\n");
    printf("我我我我我我  爱爱爱爱爱爱  你你你你你你你\n");
    return 0;
}

效果

JAVA程序员

我比C呆瓜浪漫点:

代码太长,见链接:

https://blog.csdn.net/qq_42185999/article/details/88386998

专属java程序员的浪漫

Android 开发程序员

我要在你掌心里画一条优美的弧线:

源码:https://www.jianshu.com/p/329339c58dd5

安卓傻瓜的浪漫表白,可以自定义加字

前端开发者

心动态生成,右侧时间实时更新,左边可以作为表白的语句,一个字一个字显现。

源码(算是帮别人免费推荐了一波,我心痛):https://www.html5tricks.com/html5-heart-animation.html

后台开发者

我爱你,结束。



欢迎关注技术阁,发布有趣的科技资讯,分享有料的技术干货。

记得关注哦,谢谢!

天是2019年的第一个情人节!,是的你没听错,是情人节到了,还在办公室加班的你还在加班吗?



是不是乐此不疲 ?



还在赶项目进度而 忘记吃饭甚至忘记给女朋友惊喜 的你,知道老板已经已经在为情人节狂欢了吗?



好了,这些都没有关系,谁叫你是程序猿呢,程序猿的情人节就要跟别人不一样!

程序猿要是有喜欢的人(没有也假设有),那各种表白送情意的招儿那还少吗?

什么?一脸懵逼? 下面还是给大家普及普及吧

小编和广大程序猿一样,一直认为情人节最好的礼物就是...

送你一桶代码!~~

没错,你没看错的。

| Java 程序猿 |



在生命结束之前,每天都会爱你多一点。

| C 程序猿 |



without u(没有了你),我再也不能对 the world(这个世界) say hello!

| 高级的程序猿 |



两隔的世界;

无名的信件;

短暂的停留;

长久的记忆;

说这么多,只想告诉你:我想你了。

| Python 程序猿 |



山无陵,江水为竭,冬雷震震,夏雨雪,天地合,乃敢与君绝!

| Javascript 程序猿 |



春风十里不如你!

还有... ~~~~!

| 搞 DBA的 |



把你的心、我的心,串一串,串一株幸运草,串一个同心圆...

| 搞运维的 |



每天只想对你道下晚安:做个好梦,my 甜心!

| 搞前端的 | 硬是用Html5 整出了一个3D表白图:



~~!!! 还是搞前端的有优势啊

| 后端程序猿 | 表示不服,祭出了这段代码:



也不知道是什么意思,反正小编粘贴到 Shell 客户端后就变成了这样:



| 搞架构的 |



都 三四十的人了,不敢乱表白呀 `_`!!,很有责任心呢,实在是好榜样。



| 搞临时工的 |



俺也不会啥代码的,就送几个钻戒吧..





小编呢,也偷偷找了一个有个性的诗句,可以借鉴采纳

我能抽象出整个世界...

但是我不能抽象出你...

因为你在我心中是那么的具体...

所以我的世界并不完整...

我可以重载甚至覆盖这个世界里的任何一种方法...

但是我却不能重载对你的思念...

也许命中注定了 你在我的世界里永远的烙上了静态的属性...

而我不慎调用了爱你这个方法...

当我义无返顾的把自己作为参数传进这个方法时...

我才发现爱上你是一个死循环...

它不停的返回对你的思念压入我心里的堆栈...

在这无尽的黑夜中...

我的内存里已经再也装不下别人...



谨以此篇送给还在犹豫的你,不管结果如何努力过就好,祝愿天下有情猿终有眷属!

本文大多内容摘自互联网,如有雷同请勿喷勿念,大家开心就好。