Android实现炫酷SVG动画效果
svg是眼下十分流行的图像文件格式了,svg严格来说应该是一种开放标准的矢量图形语言,使用svg格式我们能够直接用代码来描画图像,能够用不论什么文字处理工具打开svg图像。通过改变部分代码来使图像具有交互功能,并能够随时插入到HTML中通过浏览器(如火狐浏览器)来观看。使用svg格式可让你设计激动人心的、高分辨率的Web图形页面。
svg格式具备眼下网络流行的jpg和png等格式无法具备的优势:能够随意放大图形显示,但绝不会以牺牲图像质量为代价;可在svg图像中保留可编辑和可搜寻的状态;平均来讲,svg文件比其他格式的图像文件要小非常多,因而下载也非常快。
我们先来看几张Android上使用SVG的效果图:
从上面的图片看到,假设我们自己来实现这种特效,很的麻烦,只是接下来给大家介绍一个开源控件,就能够配合SVG实现这些效果。
首先我们来了解SVG文件的格式,举个样例:
<svg xmlns="http://www.w3.org/2000/svg" id="svg" class="svg" viewBox="0 0 960 480" preserveAspectRatio="xMinYMin meet">
<path fill="#B4BEC8" stroke="#B4BEC8" stroke-width="2px" stroke-miterlimit="10" d="M570.14 440.2l-29.165-28.99c-7.103-8.5-6.152-36.718-6.02-40.665H425.048c.133 3.947 1.082 32.164-6.018 40.666l-29.166 28.99c-1.237 1.404-1.712 2.505-1.623 3.37h-.054c.76 7.727 6.664 6.332 13.607 6.332H558.01c6.696 0 12.412 1.27 13.493-5.56.58-.953.274-2.282-1.364-4.14z" style="fill-opacity: 1; stroke-opacity: 0; -webkit-transition: fill-opacity 1s ease-in-out, stroke-opacity 1s ease-in-out; transition: fill-opacity 1s ease-in-out, stroke-opacity 1s ease-in-out; stroke-dasharray: 474.095184326172px, 474.095184326172px; stroke-dashoffset: 0px;"></path> <path fill="#C8D2DC" stroke="#C8D2DC" stroke-width="2px" stroke-miterlimit="10" d="M727.488 355.125c0 8.514-6.597 15.42-14.738 15.42h-465.5c-8.14 0-14.74-6.906-14.74-15.42V45.42c0-8.517 6.6-15.42 14.74-15.42h465.5c8.142 0 14.738 6.903 14.738 15.42v309.705z" style="fill-opacity: 1; stroke-opacity: 0; -webkit-transition: fill-opacity 1s ease-in-out, stroke-opacity 1s ease-in-out; transition: fill-opacity 1s ease-in-out, stroke-opacity 1s ease-in-out; stroke-dasharray: 1645.18310546875px, 1645.18310546875px; stroke-dashoffset: 0px;"></path> <path fill="#fff" stroke="#C8D2DC" stroke-width="2px" stroke-miterlimit="10" d="M489.01 343.713c-.042-4.223 3.447-6.254 3.605-6.352-1.963-2.866-5.018-3.263-6.102-3.31-2.602-.26-5.074 1.53-6.39 1.53s-3.356-1.49-5.506-1.448c-2.836.04-5.445 1.645-6.907 4.182-2.942 5.11-.75 12.672 2.116 16.814 1.4 2.02 3.072 4.305 5.268 4.22 2.114-.08 2.913-1.362 5.467-1.362 2.556 0 3.274 1.363 5.51 1.322 2.273-.04 3.716-2.064 5.105-4.098 1.61-2.35 2.273-4.63 2.313-4.748-.05-.02-4.434-1.7-4.48-6.75M484.807 331.31c1.168-1.41 1.953-3.37 1.738-5.327-1.68.068-3.713 1.12-4.916 2.53-1.08 1.247-2.027 3.245-1.77 5.16 1.87.143 3.784-.95 4.947-2.362" style="fill-opacity: 1; stroke-opacity: 0; -webkit-transition: fill-opacity 1s ease-in-out, stroke-opacity 1s ease-in-out; transition: fill-opacity 1s ease-in-out, stroke-opacity 1s ease-in-out; stroke-dasharray: 115.244583129883px, 115.244583129883px; stroke-dashoffset: 0px;"></path> <path fill="#3C4650" stroke="#3C4650" stroke-width="2px" stroke-miterlimit="10" d="M727.488 315.527V45.982c0-8.828-6.597-15.982-14.738-15.982h-465.5c-8.14 0-14.74 7.155-14.74 15.982v269.545H727.49z" style="fill-opacity: 1; stroke-opacity: 0; -webkit-transition: fill-opacity 1s ease-in-out, stroke-opacity 1s ease-in-out; transition: fill-opacity 1s ease-in-out, stroke-opacity 1s ease-in-out; stroke-dasharray: 1547.85571289063px, 1547.85571289063px; stroke-dashoffset: 0px;"></path> <path fill="#141E28" stroke="#141E28" stroke-width="2px" stroke-miterlimit="10" d="M251.2 48.887h457.205v245.52H251.2z" style="fill-opacity: 1; stroke-opacity: 0; -webkit-transition: fill-opacity 1s ease-in-out, stroke-opacity 1s ease-in-out; transition: fill-opacity 1s ease-in-out, stroke-opacity 1s ease-in-out; stroke-dasharray: 1405.44995117188px, 1405.44995117188px; stroke-dashoffset: 0px;"></path>
</svg>
上面的代码非常复杂,假设说它们是代码的话,可是我们能够注意到,这样的书写方式。有点类似于html。都是使用标签
使用最多的标签是path,也就是路径
有的人也会想到,要实现照片上的动态效果。我们能够使用Android自带的画图类和函数,复杂的曲线路径,我们能够使用path这个类来制定
那会不会SVG里面的path,事实上也是这样,那么我们就能够将SVG中的path,相应到android,然后绘制出来就好了。
SVG里面还有各种标签:
包含line直线,circle圆。rect矩形,eliipse椭圆。polygon多边形,等等
这些仅仅要我们又一个SVG文件,都能够将其转换成java代码
作为一个程序猿,我们当然不能手动去做这个工作,那就涉及两个问题,一个是SVG的解析,一个是解析后的绘制
幸运的是,已经有人完毕了这个工作。而且在Github上开源 https://github.com/geftimov/android-pathview
这篇文章将作为一个简单的样例,来使用上面的开源控件
为了解析SVG,我们须要将一个androidsvg.jar包括进我们的lib
以下我们来看这个控件的简单使用。作为一个自己定义控件,我们仅仅须要在布局文件中面加入
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:background="#ff0000"
android:layout_width="fill_parent"
android:layout_height="fill_parent"> <com.example.kaiyicky.myapplication.PathView
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/pathView"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:pathColor="@android:color/white"
app:svg="@raw/ironman_white"
app:pathWidth="5"/>
</LinearLayout>
事实上app:svg指定了一个SVG文件,我们能够把这个文章放在raw文件夹以下
然后来看Activity里面:
public class MainActivity extends FragmentActivity { @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); final PathView pathView = (PathView) findViewById(R.id.pathView);
// final Path path = makeConvexArrow(50, 100);
// pathView.setPath(path);
pathView.setFillAfter(true);
pathView.useNaturalColors();
pathView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
pathView.getPathAnimator().
delay(100).
duration(1500).
interpolator(new AccelerateDecelerateInterpolator()).
start();
}
});
} private Path makeConvexArrow(float length, float height) {
final Path path = new Path();
path.moveTo(0.0f, 0.0f);
path.lineTo(length / 4f, 0.0f);
path.lineTo(length, height / 2.0f);
path.lineTo(length / 4f, height);
path.lineTo(0.0f, height);
path.lineTo(length * 3f / 4f, height / 2f);
path.lineTo(0.0f, 0.0f);
path.close();
return path;
} }
看到凝视的部分,调用了makeConvexArraw()方法,假设我们没有在xml文件中面指定svg文件。我们也能够在代码中手动指定绘制的路径
让代码跑起来,点击屏幕。于是就实现了下面效果:
就是这么简单。至于这么制作SVG文件,大家能够找美工帮忙,使用ps和ai,能够将图片转换成SVG
最后是源代码下载地址:http://download.csdn.net/detail/kangaroo835127729/9016349
整个过程有两个类,一个是SVG解析工具类:
/**
* Util class to init and get paths from svg.
*/
public class SvgUtils {
/**
* It is for logging purposes.
*/
private static final String LOG_TAG = "SVGUtils";
/**
* All the paths with their attributes from the svg.
*/
private final List<SvgPath> mPaths = new ArrayList<SvgPath>();
/**
* The paint provided from the view.
*/
private final Paint mSourcePaint;
/**
* The init svg.
*/
private SVG mSvg; /**
* Init the SVGUtils with a paint for coloring.
*
* @param sourcePaint - the paint for the coloring.
*/
public SvgUtils(final Paint sourcePaint) {
mSourcePaint = sourcePaint;
} /**
* Loading the svg from the resources.
*
* @param context Context object to get the resources.
* @param svgResource int resource id of the svg.
*/
public void load(Context context, int svgResource) {
if (mSvg != null) return;
try {
mSvg = SVG.getFromResource(context, svgResource);
mSvg.setDocumentPreserveAspectRatio(PreserveAspectRatio.UNSCALED);
} catch (SVGParseException e) {
Log.e(LOG_TAG, "Could not load specified SVG resource", e);
}
} /**
* Draw the svg to the canvas.
*
* @param canvas The canvas to be drawn.
* @param width The width of the canvas.
* @param height The height of the canvas.
*/
public void drawSvgAfter(final Canvas canvas, final int width, final int height) {
final float strokeWidth = mSourcePaint.getStrokeWidth();
rescaleCanvas(width, height, strokeWidth, canvas);
} /**
* Render the svg to canvas and catch all the paths while rendering.
*
* @param width - the width to scale down the view to,
* @param height - the height to scale down the view to,
* @return All the paths from the svg.
*/
public List<SvgPath> getPathsForViewport(final int width, final int height) {
final float strokeWidth = mSourcePaint.getStrokeWidth();
Canvas canvas = new Canvas() {
private final Matrix mMatrix = new Matrix(); @Override
public int getWidth() {
return width;
} @Override
public int getHeight() {
return height;
} @Override
public void drawPath(Path path, Paint paint) {
Path dst = new Path(); //noinspection deprecation
getMatrix(mMatrix);
path.transform(mMatrix, dst);
paint.setAntiAlias(true);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(strokeWidth);
mPaths.add(new SvgPath(dst, paint));
}
}; rescaleCanvas(width, height, strokeWidth, canvas); return mPaths;
} /**
* Rescale the canvas with specific width and height.
*
* @param width The width of the canvas.
* @param height The height of the canvas.
* @param strokeWidth Width of the path to add to scaling.
* @param canvas The canvas to be drawn.
*/
private void rescaleCanvas(int width, int height, float strokeWidth, Canvas canvas) {
final RectF viewBox = mSvg.getDocumentViewBox(); final float scale = Math.min(width
/ (viewBox.width() + strokeWidth),
height / (viewBox.height() + strokeWidth)); canvas.translate((width - viewBox.width() * scale) / 2.0f,
(height - viewBox.height() * scale) / 2.0f);
canvas.scale(scale, scale); mSvg.renderToCanvas(canvas);
} /**
* Path with bounds for scalling , length and paint.
*/
public static class SvgPath {
/**
* Region of the path.
*/
private static final Region REGION = new Region();
/**
* This is done for clipping the bounds of the path.
*/
private static final Region MAX_CLIP =
new Region(Integer.MIN_VALUE, Integer.MIN_VALUE,
Integer.MAX_VALUE, Integer.MAX_VALUE);
/**
* The path itself.
*/
final Path path;
/**
* The paint to be drawn later.
*/
final Paint paint;
/**
* The length of the path.
*/
final float length;
/**
* The bounds of the path.
*/
final Rect bounds;
/**
* The measure of the path, we can use it later to get segment of it.
*/
final PathMeasure measure; /**
* Constructor to add the path and the paint.
*
* @param path The path that comes from the rendered svg.
* @param paint The result paint.
*/
SvgPath(Path path, Paint paint) {
this.path = path;
this.paint = paint; measure = new PathMeasure(path, false);
this.length = measure.getLength(); REGION.setPath(path, MAX_CLIP);
bounds = REGION.getBounds();
}
}
}
一个是SVG控件类:
/**
* PathView is an View that animate paths.
*/
public class PathView extends View {
/**
* Logging tag.
*/
public static final String LOG_TAG = "PathView";
/**
* The paint for the path.
*/
private Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
/**
* Utils to catch the paths from the svg.
*/
private final SvgUtils svgUtils = new SvgUtils(paint);
/**
* All the paths provided to the view. Both from Path and Svg.
*/
private List<SvgUtils.SvgPath> paths = new ArrayList<SvgUtils.SvgPath>(0);
/**
* This is a lock before the view is redrawn
* or resided it must be synchronized with this object.
*/
private final Object mSvgLock = new Object();
/**
* Thread for working with the object above.
*/
private Thread mLoader; /**
* The svg image from the raw directory.
*/
private int svgResourceId;
/**
* Object that build the animation for the path.
*/
private AnimatorBuilder animatorBuilder;
/**
* The progress of the drawing.
*/
private float progress = 0f; /**
* If the used colors are from the svg or from the set color.
*/
private boolean naturalColors;
/**
* If the view is filled with its natural colors after path drawing.
*/
private boolean fillAfter;
/**
* The width of the view.
*/
private int width;
/**
* The height of the view.
*/
private int height; /**
* Default constructor.
*
* @param context The Context of the application.
*/
public PathView(Context context) {
this(context, null);
} /**
* Default constructor.
*
* @param context The Context of the application.
* @param attrs attributes provided from the resources.
*/
public PathView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
} /**
* Default constructor.
*
* @param context The Context of the application.
* @param attrs attributes provided from the resources.
* @param defStyle Default style.
*/
public PathView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
paint.setStyle(Paint.Style.STROKE);
getFromAttributes(context, attrs);
} /**
* Get all the fields from the attributes .
*
* @param context The Context of the application.
* @param attrs attributes provided from the resources.
*/
private void getFromAttributes(Context context, AttributeSet attrs) {
final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.PathView);
try {
if (a != null) {
paint.setColor(a.getColor(R.styleable.PathView_pathColor, 0xff00ff00));
paint.setStrokeWidth(a.getFloat(R.styleable.PathView_pathWidth, 8.0f));
svgResourceId = a.getResourceId(R.styleable.PathView_svg, 0);
}
} finally {
if (a != null) {
a.recycle();
}
}
} /**
* Set paths to be drawn and animated.
*
* @param paths - Paths that can be drawn.
*/
public void setPaths(final List<Path> paths) {
for (Path path : paths) {
this.paths.add(new SvgUtils.SvgPath(path, paint));
}
synchronized (mSvgLock) {
updatePathsPhaseLocked();
}
} /**
* Set path to be drawn and animated.
*
* @param path - Paths that can be drawn.
*/
public void setPath(final Path path) {
paths.add(new SvgUtils.SvgPath(path, paint));
synchronized (mSvgLock) {
updatePathsPhaseLocked();
}
} /**
* Animate this property. It is the percentage of the path that is drawn.
* It must be [0,1].
*
* @param percentage float the percentage of the path.
*/
public void setPercentage(float percentage) {
if (percentage < 0.0f || percentage > 1.0f) {
throw new IllegalArgumentException("setPercentage not between 0.0f and 1.0f");
}
progress = percentage;
synchronized (mSvgLock) {
updatePathsPhaseLocked();
}
invalidate();
} /**
* This refreshes the paths before draw and resize.
*/
private void updatePathsPhaseLocked() {
final int count = paths.size();
for (int i = 0; i < count; i++) {
SvgUtils.SvgPath svgPath = paths.get(i);
svgPath.path.reset();
svgPath.measure.getSegment(0.0f, svgPath.length * progress, svgPath.path, true);
// Required only for Android 4.4 and earlier
svgPath.path.rLineTo(0.0f, 0.0f);
}
} @Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas); synchronized (mSvgLock) {
canvas.save();
canvas.translate(getPaddingLeft(), getPaddingTop());
final int count = paths.size();
for (int i = 0; i < count; i++) {
final SvgUtils.SvgPath svgPath = paths.get(i);
final Path path = svgPath.path;
final Paint paint1 = naturalColors ? svgPath.paint : paint;
canvas.drawPath(path, paint1);
}
fillAfter(canvas);
canvas.restore();
}
} /**
* If there is svg , the user called setFillAfter(true) and the progress is finished.
*
* @param canvas Draw to this canvas.
*/
private void fillAfter(final Canvas canvas) {
if (svgResourceId != 0 && fillAfter && progress == 1f) {
svgUtils.drawSvgAfter(canvas, width, height);
}
} @Override
protected void onSizeChanged(final int w, final int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh); if (mLoader != null) {
try {
mLoader.join();
} catch (InterruptedException e) {
Log.e(LOG_TAG, "Unexpected error", e);
}
}
if (svgResourceId != 0) {
mLoader = new Thread(new Runnable() {
@Override
public void run() { svgUtils.load(getContext(), svgResourceId); synchronized (mSvgLock) {
width = w - getPaddingLeft() - getPaddingRight();
height = h - getPaddingTop() - getPaddingBottom();
paths = svgUtils.getPathsForViewport(width, height);
updatePathsPhaseLocked();
}
}
}, "SVG Loader");
mLoader.start();
}
} @Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
if (svgResourceId != 0) {
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
setMeasuredDimension(widthSize, heightSize);
return;
} int desiredWidth = 0;
int desiredHeight = 0;
final float strokeWidth = paint.getStrokeWidth() / 2;
for (SvgUtils.SvgPath path : paths) {
desiredWidth += path.bounds.left + path.bounds.width() + strokeWidth;
desiredHeight += path.bounds.top + path.bounds.height() + strokeWidth;
}
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(widthMeasureSpec); int measuredWidth, measuredHeight; if (widthMode == MeasureSpec.AT_MOST) {
measuredWidth = desiredWidth;
} else {
measuredWidth = widthSize;
} if (heightMode == MeasureSpec.AT_MOST) {
measuredHeight = desiredHeight;
} else {
measuredHeight = heightSize;
} setMeasuredDimension(measuredWidth, measuredHeight);
} /**
* If the real svg need to be drawn after the path animation.
*
* @param fillAfter - boolean if the view needs to be filled after path animation.
*/
public void setFillAfter(final boolean fillAfter) {
this.fillAfter = fillAfter;
} /**
* If you want to use the colors from the svg.
*/
public void useNaturalColors() {
naturalColors = true;
} /**
* Animator for the paths of the view.
*
* @return The AnimatorBuilder to build the animation.
*/
public AnimatorBuilder getPathAnimator() {
if (animatorBuilder == null) {
animatorBuilder = new AnimatorBuilder(this);
}
return animatorBuilder;
} /**
* Get the path color.
*
* @return The color of the paint.
*/
public int getPathColor() {
return paint.getColor();
} /**
* Set the path color.
*
* @param color -The color to set to the paint.
*/
public void setPathColor(final int color) {
paint.setColor(color);
} /**
* Get the path width.
*
* @return The width of the paint.
*/
public float getPathWidth() {
return paint.getStrokeWidth();
} /**
* Set the path width.
*
* @param width - The width of the path.
*/
public void setPathWidth(final float width) {
paint.setStrokeWidth(width);
} /**
* Get the svg resource id.
*
* @return The svg raw resource id.
*/
public int getSvgResource() {
return svgResourceId;
} /**
* Set the svg resource id.
*
* @param svgResource - The resource id of the raw svg.
*/
public void setSvgResource(int svgResource) {
svgResourceId = svgResource;
} /**
* Object for building the animation of the path of this view.
*/
public static class AnimatorBuilder {
/**
* Duration of the animation.
*/
private int duration = 350;
/**
* Interpolator for the time of the animation.
*/
private Interpolator interpolator;
/**
* The delay before the animation.
*/
private int delay = 0;
/**
* ObjectAnimator that constructs the animation.
*/
private final ObjectAnimator anim;
/**
* Listener called before the animation.
*/
private ListenerStart listenerStart;
/**
* Listener after the animation.
*/
private ListenerEnd animationEnd;
/**
* Animation listener.
*/
private PathViewAnimatorListener pathViewAnimatorListener; /**
* Default constructor.
*
* @param pathView The view that must be animated.
*/
public AnimatorBuilder(final PathView pathView) {
anim = ObjectAnimator.ofFloat(pathView, "percentage", 0.0f, 1.0f);
} /**
* Set the duration of the animation.
*
* @param duration - The duration of the animation.
* @return AnimatorBuilder.
*/
public AnimatorBuilder duration(final int duration) {
this.duration = duration;
return this;
} /**
* Set the Interpolator.
*
* @param interpolator - Interpolator.
* @return AnimatorBuilder.
*/
public AnimatorBuilder interpolator(final Interpolator interpolator) {
this.interpolator = interpolator;
return this;
} /**
* The delay before the animation.
*
* @param delay - int the delay
* @return AnimatorBuilder.
*/
public AnimatorBuilder delay(final int delay) {
this.delay = delay;
return this;
} /**
* Set a listener before the start of the animation.
*
* @param listenerStart an interface called before the animation
* @return AnimatorBuilder.
*/
public AnimatorBuilder listenerStart(final ListenerStart listenerStart) {
this.listenerStart = listenerStart;
if (pathViewAnimatorListener == null) {
pathViewAnimatorListener = new PathViewAnimatorListener();
anim.addListener(pathViewAnimatorListener);
}
return this;
} /**
* Set a listener after of the animation.
*
* @param animationEnd an interface called after the animation
* @return AnimatorBuilder.
*/
public AnimatorBuilder listenerEnd(final ListenerEnd animationEnd) {
this.animationEnd = animationEnd;
if (pathViewAnimatorListener == null) {
pathViewAnimatorListener = new PathViewAnimatorListener();
anim.addListener(pathViewAnimatorListener);
}
return this;
} /**
* Starts the animation.
*/
public void start() {
anim.setDuration(duration);
anim.setInterpolator(interpolator);
anim.setStartDelay(delay);
anim.start();
} /**
* Animation listener to be able to provide callbacks for the caller.
*/
private class PathViewAnimatorListener implements Animator.AnimatorListener { @Override
public void onAnimationStart(Animator animation) {
if (listenerStart != null) listenerStart.onAnimationStart();
} @Override
public void onAnimationEnd(Animator animation) {
if (animationEnd != null) animationEnd.onAnimationEnd();
} @Override
public void onAnimationCancel(Animator animation) { } @Override
public void onAnimationRepeat(Animator animation) { }
} /**
* Called when the animation start.
*/
public interface ListenerStart {
/**
* Called when the path animation start.
*/
void onAnimationStart();
} /**
* Called when the animation end.
*/
public interface ListenerEnd {
/**
* Called when the path animation end.
*/
void onAnimationEnd();
}
}
}
Android实现炫酷SVG动画效果的更多相关文章
- Android ViewPager+属性动画 实现炫酷视差动画效果
ViewPager有一个setPageTransform()方法可以实现很多酷炫的动画效果 先来个仿QQ的侧滑面板效果 vp.setPageTransformer(true, new PageTran ...
- Android一个炫酷的树状图组织架构图开源控件实现过程
Android一个炫酷的树状图组织架构图开源控件 文章目录 [1 简介] [2 效果展示] [3 使用步骤] [4 实现基本布局流程] [5 实现自由放缩及拖动] [6 实现添加删除及节点动画] [7 ...
- css3 之炫酷的loading效果
css3 之炫酷的loading效果 今天实现了一个炫酷的loading效果,基本全用css来实现,主要练习一下css3的熟练运用 js需要引入jquery 只用到了一点点js 先看效果图 html: ...
- 一款炫酷Loading动画--载入失败
简单介绍 上一篇文章一款炫酷Loading动画–载入成功.给大家介绍了成功动画的绘制过程,这篇文章将接着介绍载入失败特效的制作. 相比成功动画,有了前面的经验,失败动画的过程就显得比較简单了. 动画结 ...
- Android利用温度传感器实现带动画效果的电子温度计
概述 Android利用温度传感器实现带动画效果的电子温度计. 详细 代码下载:http://www.demodashi.com/demo/10631.html 一.准备工作 需要准备一部带有温度传感 ...
- 我的Android进阶之旅------>Android利用温度传感器实现带动画效果的电子温度计
要想实现带动画效果的电子温度计,需要以下几个知识点: 1.温度传感器相关知识. 2.ScaleAnimation动画相关知识,来进行水印刻度的缩放效果. 3.android:layout_weight ...
- 一个炫酷的Actionbar效果
今天在网上看到一个炫酷的Actionbar效果,一个老外做的DEMO,目前很多流行的app已经加入了这个效果. 当用户初始进入该界面的时候,为一个透明的 ActiionBar ,这样利用充分的空间显示 ...
- uwp - 做一个相对炫酷的动画按钮/按钮动画
原文:uwp - 做一个相对炫酷的动画按钮/按钮动画 看腻了系统自带的button animation何不尝试下自定义一个较为炫酷的动画顺便提升用户体验.效果图: 动画分为几个部分,分别是:内圆从中心 ...
- >炫酷的计时器效果Canvas绘图与动画<
>炫丽的计时器效果Canvas绘图与动画< 虽然我是学习java的,但是因为最近使用html5的关系,多学习了一下前端知识. 现在,我要介绍的计时器是十分炫酷的,使用画布完成. 喜欢htm ...
随机推荐
- oracle odbc 驱动安装(不安装oracle客户端)
1.下载odbc驱动 需要下载两个东西 instantclient-basiclite-nt-12.1.0.1.0.zip instantclient-odbc-nt-12.1.0.1.0.zip 由 ...
- Linux下的GitHub安装与简单配置教程 ~ 转载
Linux下的GitHub安装与简单配置教程 1.GitHub简介 Git是一个分布式版本控制系统,与其相对的是CVS.SVN等集中式的版本控制系统. 2.Git的安装 1)安装Git a.查看与 ...
- SQL的四种连接查询(转)
原文转自 https://blog.csdn.net/wangjingna/article/details/48765931
- UVA 10330 Power Transmission
题意:懒得打了.LUCKY CAT 里有 http://163.32.78.26/homework/q10330.htm 第一个网络流题目.每个节点都有一个容量值.需要拆点.拆成i - > i ...
- dnspod 批量添加记录
#!/usr/bin/python #-*- coding: utf-8 -*- import os,requests,json import re,xlsxwriter,time import xl ...
- hdu 5105(数学题)
Math Problem Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Tota ...
- VS2017源代码版本管理
VS2017源代码版本管理有两种方式:Git(代码提交到服务器)和Team Foundation Server(代码提交到局域网) 一.Git版本管理(上传到码云服务器https://gitee.co ...
- (寒假集训)Watering the Fields (最小生成树)
Watering the Fields 时间限制: 1 Sec 内存限制: 64 MB提交: 26 解决: 10[提交][状态][讨论版] 题目描述 Due to a lack of rain, ...
- POJ 1044: Date bugs
题目描述 There are rumors that there are a lot of computers having a problem with the year 2000. As they ...
- (转)Unity3D协同程序(Coroutine)
一.什么是协同程序 协同程序,即在主程序运行时同时开启另一段逻辑处理,来协同当前程序的执行.换句话说,开启协同程序就是开启一个线程. 二.协同程序的开启与终止 在Unity3D中,使用MonoBeha ...