仿饿了么加入购物车旋转控件-自带闪转腾挪动画的按钮

张旭童 张旭童     2022-08-20     401

关键词:

本篇文章已授权微信公众号 guolin_blog (郭霖)独家发布
转载请标明出处:
http://blog.csdn.net/zxt0601/article/details/54235736
本文出自:【张旭童的博客】(http://blog.csdn.net/zxt0601)
代码传送门:喜欢的话,随手点个star。多谢
https://github.com/mcxtzhang/AnimShopButton

经济上支持我 or 想通过视频看我是怎么实现的:

http://edu.csdn.net/course/detail/3898

概述

上文,酷炫Path动画已经预告了,今天给大家带来的是利用 纯自定义View,实现的仿饿了么加入购物车控件,自带闪转腾挪动画的按钮。
效果图如下:

图1 项目中使用的效果,考虑到了View回收复用
并且可以看到在RecyclerView中使用,切换LayoutManager也是没有问题的,
项目中使用的效果

图2 Demo效果,测试各种属性值
图2 Demo效果,测试各种属性值

注意,本控件非继承自ViewGroup,而是纯自定义View实现。理由如下:

  • 1 减少布局层级,从而提高性能
  • 2 文字和图形纯draw,用到什么draw什么,没有其他的额外工作,也间接提高性能。
  • 3 纯自定义View难度更高,更有实(装)践(B)的意义

1 减少布局层次,很好理解,ViewGroup内嵌套几个TextView、ImageV这里写代码片iew也可以实现这个效果,然而这会使布局层次多了一级,并且内部要嵌套多个控件,层级越多,控件越多,绘制的就越慢,在列表中对性能的影响更大。

2 别小看了“小小”的TextView和的ImageView,其实它们有很多的属性和特性在本例中是不必要的,举个例子,查看源码,TextView一万多行ondraw()方法有一百多行, ImageView1588行,这么多行代码都是我们需要的吗?直接使用这些现成的控件嵌套实现,其实性能不如我们用到什么draw什么。唯一的好处可能就是比较简单了。(其实TextView的性能是不高的)

3 纯自定义Viewdraw出这些需要的元素,并且还要考虑动画,以及点击各区域的监听,实现起来还是有一些难度的,但我们多写一些有难度的代码才能提高水平。

如何使用

伸手党福利:讲解实现前,先看一下如何使用 以及支持的属性等。

使用

xml:

    <!--使用默认UI属性-->
    <com.mcxtzhang.lib.AnimShopButton
        android:id="@+id/btn1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:maxCount="3"/>
    <!--设置了两圆间距-->
    <com.mcxtzhang.lib.AnimShopButton
        android:id="@+id/btn2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:count="3"
        app:gapBetweenCircle="90dp"
        app:maxCount="99"/>
    <!--仿饿了么-->
    <com.mcxtzhang.lib.AnimShopButton
        android:id="@+id/btnEle"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:addEnableBgColor="#3190E8"
        app:addEnableFgColor="#ffffff"
        app:hintBgColor="#3190E8"
        app:hintBgRoundValue="15dp"
        app:hintFgColor="#ffffff"
        app:maxCount="99"/>

注意:
加减点击后,具体的操作,要根据业务的不同来编写了,设计到实际的购物车可能还有写数据库操作,或者请求接口等,要操作成功后才执行动画、或者修改count,这一块代码每个人写法可能不同。
使用时,可以重写onDelClick()onAddClick()方法,并在合适的时机回调onCountAddSuccess()onCountDelSuccess()以执行动画。

效果图如图2.

支持的属性

nameformatdescription中文解释
isAddFillModebooleanPlus button is opened Fill mode default is stroke (false)加按钮是否开启fill模式 默认是stroke(false)
addEnableBgColorcolorThe background color of the plus button加按钮的背景色
addEnableFgColorcolorThe foreground color of the plus button加按钮的前景色
addDisableBgColorcolorThe background color when the button is not available加按钮不可用时的背景色
addDisableFgColorcolorThe foreground color when the button is not available加按钮不可用时的前景色
isDelFillModebooleanPlus button is opened Fill mode default is stroke (false)减按钮是否开启fill模式 默认是stroke(false)
delEnableBgColorcolorThe background color of the minus button减按钮的背景色
delEnableFgColorcolorThe foreground color of the minus button减按钮的前景色
delDisableBgColorcolorThe background color when the button is not available减按钮不可用时的背景色
delDisableFgColorcolorThe foreground color when the button is not available减按钮不可用时的前景色
radiusdimensionThe radius of the circle圆的半径
circleStrokeWidthdimensionThe width of the circle圆圈的宽度
lineWidthdimensionThe width of the line (+ - sign)线(+ - 符号)的宽度
gapBetweenCircledimensionThe spacing between two circles两个圆之间的间距
numTextSizedimensionThe textSize of draws the number绘制数量的textSize
maxCountintegermax count最大数量
countintegercurrent count当前数量
hintTextstringThe hint text when number is 0数量为0时,hint文字
hintBgColorcolorThe hint background when number is 0数量为0时,hint背景色
hintFgColorcolorThe hint foreground when number is 0数量为0时,hint前景色
hingTextSizedimensionThe hint text size when number is 0数量为0时,hint文字大小
hintBgRoundValuedimensionThe background fillet value when number is 0数量为0时,hint背景圆角值

这么多属性够你用了吧。

下面看重点的实现吧,Let’s Go!.

实现解剖

关于自定义View的基础,这里不再赘述。
如果阅读时有不明白的,建议下载源码边看边读,或者学习自定义View基础知识后再阅读本文。
代码传送门:喜欢的话,随手点个star。多谢
https://github.com/mcxtzhang/AnimShopButton


我们捡重点说,无非是绘制。
绘制的重点,这里分三块:

  • 静态绘制。(分两块:加减按钮和数量、hint提示文字和背景)
  • 第一层。(加减按钮和数量)以及它的旋转、位移、透明度动画
  • 第二层。(hint区域)以及它的伸展收缩动画

除了绘制以外的重点是:

  • 由于采用了完全的自定义View去实现这么一个“组合控件效果”,则点击事件的监听需要自己处理。
  • 在回收复用的列表中使用时,列表滑动,如何正确显示UI。

静态绘制

静态绘制就是最基本的自定义View知识,绘制圆圈(Circle)、线段(Line)、数字(Text)以及圆角矩形(RoundRect),值得注意的是,
要考虑到 避免overDraw和动画的需求,
我们要绘制的两层应该是互斥关系。

剥离掉动画代码,大致如下(基本都是draw代码,可以快速阅读):

@Override
    protected void onDraw(Canvas canvas) {
        if (isHintMode) {
            //hint 展开
            //背景
            mHintPaint.setColor(mHintBgColor);
            RectF rectF = new RectF(mLeft, mTop
                    , mWidth - mCircleWidth, mHeight - mCircleWidth);
            canvas.drawRoundRect(rectF, mHintBgRoundValue, mHintBgRoundValue, mHintPaint);
            //前景文字
            mHintPaint.setColor(mHintFgColor);
            // 计算Baseline绘制的起点X轴坐标
            int baseX = (int) (mWidth / 2 - mHintPaint.measureText(mHintText) / 2);
            // 计算Baseline绘制的Y坐标
            int baseY = (int) ((mHeight / 2) - ((mHintPaint.descent() + mHintPaint.ascent()) / 2));
            canvas.drawText(mHintText, baseX, baseY, mHintPaint);
        } else {
            //左边
            //背景 圆
            if (mCount > 0) {
                mDelPaint.setColor(mDelEnableBgColor);
            } else {
                mDelPaint.setColor(mDelDisableBgColor);
            }
            mDelPaint.setStrokeWidth(mCircleWidth);
            mDelPath.reset();
            mDelPath.addCircle(mLeft + mRadius, mTop + mRadius, mRadius, Path.Direction.CW);
            mDelRegion.setPath(mDelPath, new Region(mLeft, mTop, mWidth - getPaddingRight(), mHeight - getPaddingBottom()));
            canvas.drawPath(mDelPath, mDelPaint);

            //前景 -
            if (mCount > 0) {
                mDelPaint.setColor(mDelEnableFgColor);
            } else {
                mDelPaint.setColor(mDelDisableFgColor);
            }
            mDelPaint.setStrokeWidth(mLineWidth);
            canvas.drawLine(-mRadius / 2, 0,
                    +mRadius / 2, 0,
                    mDelPaint);

            //数量
            //是没有动画的普通写法,x left, y baseLine
            canvas.drawText(mCount + "", mLeft + mRadius * 2, mTop + mRadius - (mFontMetrics.top + mFontMetrics.bottom) / 2, mTextPaint);

            //右边
            //背景 圆
            if (mCount < mMaxCount) {
                mAddPaint.setColor(mAddEnableBgColor);
            } else {
                mAddPaint.setColor(mAddDisableBgColor);
            }
            mAddPaint.setStrokeWidth(mCircleWidth);
            float left = mLeft + mRadius * 2 + mGapBetweenCircle;
            mAddPath.reset();
            mAddPath.addCircle(left + mRadius, mTop + mRadius, mRadius, Path.Direction.CW);
            mAddRegion.setPath(mAddPath, new Region(mLeft, mTop, mWidth - getPaddingRight(), mHeight - getPaddingBottom()));
            canvas.drawPath(mAddPath, mAddPaint);
            //前景 +
            if (mCount < mMaxCount) {
                mAddPaint.setColor(mAddEnableFgColor);
            } else {
                mAddPaint.setColor(mAddDisableFgColor);
            }
            mAddPaint.setStrokeWidth(mLineWidth);
            canvas.drawLine(left + mRadius / 2, mTop + mRadius, left + mRadius / 2 + mRadius, mTop + mRadius, mAddPaint);
            canvas.drawLine(left + mRadius, mTop + mRadius / 2, left + mRadius, mTop + mRadius / 2 + mRadius, mAddPaint);
        }
    }

根据isHintMode 布尔值变量,区分是绘制第二层(Hint层)或者第一层(加减按钮层)。

绘制第二层时没啥好说的,就是利用canvas.drawRoundRect,绘制圆角矩形,然后canvas.drawText绘制hint。
(如果圆角的值足够大,矩形的宽度足够小,就变成了圆形。)

绘制第一层时,要根据当前的数量选择不同的颜色,注意在绘制加减按钮的圆圈时,我们是用Path绘制的,这是因为我们还需要用Path构建Region类,这个类就是我们监听点击区域的重点

点击事件的监听

在讲解动画之前,我们先说说如何监听点击的区域,因为本控件的动画是和加减数量息息相关的,而数量的加减是由点击相应”+ - 按钮”区域触发的。
所以我们的监听按钮的点击事件,其实就是监听相应的”+ - 按钮”区域

上一节中,我们在绘制”+ - 按钮”区域时,通过Path,构建了两个Region类,Region类有个contains(int x, int y)方法如下,通过传入对应触摸的x、y坐标,就可知道知否点击了相应区域

    /**
     * Return true if the region contains the specified point
     */
    public native boolean contains(int x, int y);

知道了这一点,再写这部分代码就相当简单了:

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        int action = event.getAction();
        switch (action) {
            case MotionEvent.ACTION_DOWN:
                //hint模式
                if (isHintMode) {
                    onAddClick();
                    return true;
                } else {
                    if (mAddRegion.contains((int) event.getX(), (int) event.getY())) {
                        onAddClick();
                        return true;
                    } else if (mDelRegion.contains((int) event.getX(), (int) event.getY())) {
                        onDelClick();
                        return true;
                    }
                }
                break;
            case MotionEvent.ACTION_MOVE:
                break;
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                break;
        }
        return super.onTouchEvent(event);
    }

hint模式时,我们可以认为控件所有范围都是“+”的有效区域

而在非hint模式时,根据上一节构建的mAddRegionmDelRegion去判断。

判断确认点击后,具体的操作,要根据业务的不同来编写了,设计到实际的购物车可能还有写数据库操作,或者请求接口等,要操作成功后才执行动画、或者修改count,这一块代码每个人写法可能不同。
使用时,可以重写onDelClick()onAddClick()方法,并在合适的时机回调onCountAddSuccess()onCountDelSuccess()以执行动画。

本文如下编写:

    protected void onDelClick() {
        if (mCount > 0) {
            mCount--;
            onCountDelSuccess();
        }
    }

    protected void onAddClick() {
        if (mCount < mMaxCount) {
            mCount++;
            onCountAddSuccess();
        } else {
        }
    }

    /**
     * 数量增加成功后,使用者回调
     */
    public void onCountAddSuccess() {
        if (mCount == 1) {
            cancelAllAnim();
            mAnimReduceHint.start();
        } else {
            mAnimFraction = 0;
            invalidate();
        }
    }

    /**
     * 数量减少成功后,使用者回调
     */
    public void onCountDelSuccess() {
        if (mCount == 0) {
            cancelAllAnim();
            mAniDel.start();
        } else {
            mAnimFraction = 0;
            invalidate();
        }
    }

动画的实现

这里会用到两个变量:

    //动画的基准值 动画:减 0~1, 加 1~0 
    // 普通状态下是0
    protected float mAnimFraction;

    //提示语收缩动画 0-1 展开1-0
    //普通模式时,应该是1, 只在 isHintMode true 才有效
    protected float mAnimExpandHintFraction;

依次分析有哪些动画:

Hint动画

主要是圆角矩形的展开、收缩
固定right、bottom,当展开时,不断减少矩形的左起点left坐标值,则整个矩形宽度变大,呈现展开。收缩时相反。
代码:

            //背景
            mHintPaint.setColor(mHintBgColor);
            RectF rectF = new RectF(mLeft + (mWidth - mRadius * 2) * mAnimExpandHintFraction, mTop
                    , mWidth - mCircleWidth, mHeight - mCircleWidth);
            canvas.drawRoundRect(rectF, mHintBgRoundValue, mHintBgRoundValue, mHintPaint);

减按钮动画

看起来是旋转、位移、透明度
那么对于背景的圆圈来说,我们只需要位移、透明度。因为它本身是个圆,就不要旋转了。
代码:

            //动画 mAnimFraction :减 0~1, 加 1~0 ,
            //动画位移Max,
            float animOffsetMax = (mRadius * 2 +mGapBetweenCircle);
            //透明度动画的基准
            int animAlphaMax = 255;
            int animRotateMax = 360;

            //左边
            //背景 圆
            mDelPaint.setAlpha((int) (animAlphaMax * (1 - mAnimFraction)));
            mDelPath.reset();
            //改变圆心的X坐标,实现位移
            mDelPath.addCircle(animOffsetMax * mAnimFraction + mLeft + mRadius, mTop + mRadius, mRadius, Path.Direction.CW);
            canvas.drawPath(mDelPath, mDelPaint);

对于前景的“-”号来说,旋转、位移、透明度都需要做。
这里我们利用canvas.translate() canvas.rotate 做旋转和位移动画,别忘了 canvas.save()canvas.restore()恢复画布的状态。(透明度在上面已经设置过了。)

            //前景 -
            //旋转动画
            canvas.save();
            canvas.translate(animOffsetMax * mAnimFraction + mLeft + mRadius, mTop + mRadius);
            canvas.rotate((int) (animRotateMax * (1 - mAnimFraction)));
            canvas.drawLine(-mRadius / 2, 0,
                    +mRadius / 2, 0,
                    mDelPaint);
            canvas.restore();

数量的动画

看起来也是旋转、位移、透明度。同样是利用canvas.translate() canvas.rotate 做旋转和位移动画。

            //数量
            canvas.save();
            //平移动画
            canvas.translate(mAnimFraction * (mGapBetweenCircle / 2 - mTextPaint.measureText(mCount + "") / 2 + mRadius), 0);
            //旋转动画,旋转中心点,x 是绘图中心,y 是控件中心
            canvas.rotate(360 * mAnimFraction,
                mGapBetweenCircle / 2 + mLeft + mRadius * 2 ,
                    mTop + mRadius);
            //透明度动画
            mTextPaint.setAlpha((int) (255 * (1 - mAnimFraction)));
            //是没有动画的普通写法,x left, y baseLine
            canvas.drawText(mCount + "",  mGapBetweenCircle / 2 - mTextPaint.measureText(mCount + "") / 2 + mLeft + mRadius * 2, mTop + mRadius - (mFontMetrics.top + mFontMetrics.bottom) / 2, mTextPaint);
            canvas.restore();

动画的定义:

动画是在View初始化时就定义好的,执行顺序:

  • 数量增加,0-1时,先收缩Hint(第二层)mAnimReduceHint执行,完毕后执行减按钮(第一层)进入的动画mAnimAdd
  • 数量减少,1-0时,先执行减按钮退出的动画mAniDel,再伸展Hint动画mAnimExpandHint,完毕后,显示hint文字。

代码如下:

        //动画 +
        mAnimAdd = ValueAnimator.ofFloat(1, 0);
        mAnimAdd.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                mAnimFraction = (float) animation.getAnimatedValue();
                invalidate();
            }
        });
        mAnimAdd.setDuration(350);

        //提示语收缩动画 0-1
        mAnimReduceHint = ValueAnimator.ofFloat(0, 1);
        mAnimReduceHint.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                mAnimExpandHintFraction = (float) animation.getAnimatedValue();
                invalidate();
            }
        });
        mAnimReduceHint.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                if (mCount == 1) {
                    //然后底色也不显示了
                    isHintMode = false;
                }
                if (mCount == 1) {
                    Log.d(TAG, "现在还是1 开始收缩动画");
                    if (mAnimAdd != null && !mAnimAdd.isRunning()) {
                        mAnimAdd.start();
                    }
                }
            }

            @Override
            public void onAnimationStart(Animator animation) {
                if (mCount == 1) {
                    //先不显示文字了
                    isShowHintText = false;
                }
            }
        });
        mAnimReduceHint.setDuration(350);

        //动画 -
        mAniDel = ValueAnimator.ofFloat(0, 1);
        mAniDel.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                mAnimFraction = (float) animation.getAnimatedValue();
                invalidate();
            }
        });
        //1-0的动画
        mAniDel.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                if (mCount == 0) {
                    Log.d(TAG, "现在还是0onAnimationEnd() called with: animation = [" + animation + "]");
                    if (mAnimExpandHint != null && !mAnimExpandHint.isRunning()) {
                        mAnimExpandHint.start();
                    }
                }
            }
        });
        mAniDel.setDuration(350);
        //提示语展开动画
        //分析这个动画,最初是个圆。 就是left 不断减小
        mAnimExpandHint = ValueAnimator.ofFloat(1, 0);
        mAnimExpandHint.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                mAnimExpandHintFraction = (float) animation.getAnimatedValue();
                invalidate();
            }
        });
        mAnimExpandHint.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                if (mCount == 0) {
                    isShowHintText = true;
                }
            }
            @Override
            public void onAnimationStart(Animator animation) {
                if (mCount == 0) {
                    isHintMode = true;
                }
            }
        });
        mAnimExpandHint.setDuration(350);

针对复用机制的处理

因为我们的购物车控件肯定会用在列表中,不管你用ListView还是RecyclerView,都会涉及到复用的问题。
复用给我们带来一个麻烦的地方就是,我们要处理好一些属性状态值,否则UI上会有问题。

可以从两处下手处理:

onMeasure

列表复用时,依然会回调onMeasure()方法,所以在这里初始化一些UI显示的参数。
这里顺带将适配wrap_content 的代码也一同贴上:

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int wMode = MeasureSpec.getMode(widthMeasureSpec);
        int wSize = MeasureSpec.getSize(widthMeasureSpec);
        int hMode = MeasureSpec.getMode(heightMeasureSpec);
        int hSize = MeasureSpec.getSize(heightMeasureSpec);
        switch (wMode) {
            case MeasureSpec.EXACTLY:
                break;
            case MeasureSpec.AT_MOST:
                //不超过父控件给的范围内,自由发挥
                int computeSize = (int) (getPaddingLeft() + mRadius * 2 +mGapBetweenCircle + mRadius * 2 + getPaddingRight() + mCircleWidth * 2);
                wSize = computeSize < wSize ? computeSize : wSize;
                break;
            case MeasureSpec.UNSPECIFIED:
                //自由发挥
                computeSize = (int) (getPaddingLeft() + mRadius * 2 + mGapBetweenCircle + mRadius * 2 + getPaddingRight() + mCircleWidth * 2);
                wSize = computeSize;
                break;
        }
        switch (hMode) {
            case MeasureSpec.EXACTLY:
                break;
            case MeasureSpec.AT_MOST:
                int computeSize = (int) (getPaddingTop() + mRadius * 2 + getPaddingBottom() + mCircleWidth * 2);
                hSize = computeSize < hSize ? computeSize : hSize;
                break;
            case MeasureSpec.UNSPECIFIED:
                computeSize = (int) (getPaddingTop() + mRadius * 2 + getPaddingBottom() + mCircleWidth * 2);
                hSize = computeSize;
                break;
        }
        setMeasuredDimension(wSize, hSize);

        //复用时会走这里,所以初始化一些UI显示的参数
        mAnimFraction = 0;
        initHintSettings();
    }
    /**
     * 根据当前count数量 初始化 hint提示语相关变量
     */
    private void initHintSettings() {
        if (mCount == 0) {
            isHintMode = true;
            isShowHintText = true;
            mAnimExpandHintFraction = 0;
        } else {
            isHintMode = false;
            isShowHintText = false;
            mAnimExpandHintFraction = 1;
        }
    }

在改变count时

一般在onBindViewHolder()或者getView()时,都会对本控件重新设置count值,count改变时,当然也是需要根据count进行属性值的调整。
且此时如果View正在做动画,应该停止这些动画。

    /**
     * 设置当前数量
     * @param count
     * @return
     */
    public AnimShopButton setCount(int count) {
        mCount = count;
        //先暂停所有动画
        if (mAnimAdd != null && mAnimAdd.isRunning()) {
            mAnimAdd.cancel();
        }
        if (mAniDel != null && mAniDel.isRunning()) {
            mAniDel.cancel();
        }
        //复用机制的处理
        if (mCount == 0) {
            // 0 不显示 数字和-号
            mAnimFraction = 1;
        } else {
            mAnimFraction = 0;
        }
        initHintSettings();
        return this;
    }

总结

代码传送门:喜欢的话,随手点个star。多谢
https://github.com/mcxtzhang/AnimShopButton

经济上支持我 or 想通过视频看我是怎么实现的:

http://edu.csdn.net/course/detail/3898

我在实现这个控件时,觉得难度相对大的地方在于做动画时,“-”按钮和数量的旋转动画,如何确定正确的坐标值。因为将text绘制的居中本身就有一些注意事项在里面,再涉及到动画,难免蒙圈。需要多计算,多试验

还有就是观察饿了么的效果,将hint区域的动画利用改变RoundRect的宽度去实现。起初没有想到,也是思考了一会如何去做。这是属于分析、拆解动画遇到的问题。

除了绘制以外的重点是:

  • 利用Region监听区域点击事件
  • 复用的列表,如何正确显示UI。
  • 动画次序以及考虑到复用时,在合适的地方取消动画

尽情在项目中使用它吧,有问题随时gayhub给我反馈。

通过sdk工具查看饿了么,它其实是用TextViewImageView组合实现的。另外我十分怀疑它没有封装成控件,因为在列表页和详情页的交互,以及动画居然略有不同, 在详情页,仔细看由0-1时,它右边的 + 按钮的动画居然会闪一下,在列表页却没有,很是不解。

看大神们都有QQ群,
向他们靠齐。
我也建了个QQ搞基交流群:
557266366 。

转载请标明出处:
http://blog.csdn.net/zxt0601/article/details/54235736
本文出自:【张旭童的博客】(http://blog.csdn.net/zxt0601)
代码传送门:喜欢的话,随手点个star。多谢
https://github.com/mcxtzhang/AnimShopButton

【css动画】饿了么加入购物车抛物线动画实现

...肥宅,手动滑稽),或者在淘宝购物的时候,将商品加入购物车时会有一个很炫酷的动画,如下图饿了么点餐动画:所以百度了一下前端使用css实现这个效果,然后就自己就照葫芦画瓢的写了一个小小的demo,完全当作学习了一... 查看详情

仿饿了么购物车下单效果

仿饿了么购物车下单效果前一段由于新项目需要,开发一个类似饿了么购物车下单效果,电商类、外卖类、点餐类项目都可以用的上,废话不多说请看效果。效果图如下:主要的功能:就是左侧展示分类,右侧展示分类下商品的,... 查看详情

饿了么购物车动画(代码片段)

前言之前用饿了么印象最深刻的是联动菜单和小球飞入购物车动画,所以想看看别人是怎么实现的,但是看了很多仿饿了么的demo都是实现了一个完整的大的项目,要找到那个小模块很麻烦,所以自己将联动菜单和动画提取出来... 查看详情

仿饿了么首页导航栏(viewpager)

1、需求分析在饿了么首页中我们能看到这样的布局(如下图)。红框内是一个可以左右滑动的页面,每一个页面类似于九宫格,都有可供点击图标。对于这样的布局,我在网上找了很久都没有找到相关的名称,所以我这里暂时... 查看详情

android自定义控件demo集合

...完成,俗称占个坑~持续更新中…github地址饿了么加入购物车按钮仿支付宝支付成功动画波浪效果进度条(正弦函数实现+贝塞尔曲线实现)竖排文字多功能按钮Roun 查看详情

css贝塞尔曲线模仿饿了么购物车小球动画

在线观看贝塞尔曲线值:传送门在线观看动画效果:传送门代码:<!DOCTYPEhtml><html><head><metacharset="utf-8"><metaname="viewport"content="width=device-width"><title>JSBin</title><style> 查看详情

高仿饿了么mock本地数据(代码片段)

新版webpack.dev.conf.js配置本地数据访问://引入express模块constexpress=require(‘express‘)//创建express对象constapp=express()//引入请求文件加载本地数据文件constappData=require(‘../data.json‘)//获取对应的本地数据constseller=appData.sellerconstg 查看详情

vue.js高仿饿了么外卖app

1.架构从传统的MVC向RESTAPI+前端MV*迁移参考链接:http://blog.csdn.net/broadview2006/article/details/8615055http://blog.csdn.net/u013628152/article/details/42709033MV*包括:MVC、MVP、MVVMvue.js是MVVM框架 2.Iconmoon制作图标字体2 查看详情

vue2高仿饿了么app

Github地址: https://github.com/ccyinghua/appEleme-project 一、构建项目所用:vueinitwebpackappEleme-projectnpmrundevcnpminstallnode-sass--save-devcnpminstallsass-loader--save-dev//sass-loader依赖于node-sas 查看详情

基于vue2+nuxt构建的高仿饿了么(2018版)(代码片段)

前言高仿饿了么,以nuxt作为vue的服务端渲染,适合刚接触或者准备上vuessr的同学参考和学习项目地址如遇网络不佳,请移步国内镜像加速节点效果演示查看demo请戳这里(请用chrome手机模式预览)移动端扫描下方二维码API接口文... 查看详情

基于vue来开发一个仿饿了么的外卖商城(代码片段)

一、抽出头部作为一个组件,在底部导航的时候可以相应的显示不同的标题技术点:使用slot进行组件间的通信;父组件给子组件传值(子组件里面通过props接收父组件传过来的数据)查看链接:https://blog.csdn.net/sinat_17775997/article/... 查看详情

基于vue来开发一个仿饿了么的外卖商城(代码片段)

一、准备工作1.大前提:已安装好node、npm、vue、vue-cli、stylus(此项目使用stylus来编译)2.开发软件:GoogleChrome(建议安装插件vue-devtools,方便调试),webstorm/sublimeText/VSCode(推荐使用webstorm,sublime和VSCode需要安装相应的插件)3.... 查看详情

android自定义控件demo集合

...完成,俗称占个坑~持续更新中…github地址饿了么加入购物车按钮仿支付宝支付成功动画波浪效果进度条(正弦函数实现+贝塞尔曲线实现)竖排文字多功能按钮RoundLoadingView可自定义圆角、正常/点击/不可用时的背景... 查看详情

仿饿了么项目-vue-cli开启项目

环境搭建安装nodeJs在用vue-cli开启项目之前,首先我们需要安装Node环境,安装Node很简单,只需要去官网下载http://nodejs.cn/download/,下载完成后点击安装,安装过程很简单,一直next即可,安装完成会自动添加node及npm环境变量。检... 查看详情

在vue中优雅的使用localstrong

...使用场景,比如京东购物在未登陆的状态下,把商品加入购物车,收藏某个商品。当我们把url复制到另外一个浏览器,购物车就是空的。以下是一个简单的商品收藏小demo,让我们在未登陆的状态下收藏某个商品。这个例子是学习v... 查看详情

基于springboot+redis+vue仿饿了么外卖系统(后台+移动端)

优质文章,第一时间送达 六、运行效果前端展示(点击图片查看大图)后台管理(点击图片查看大图) 七、源码下载源码下载云盘链接:链接:https://pan.baidu.com/s/1djb2BmP3S6l9OfzT62Nd6A提取码:lry9复制这... 查看详情

饿了么交易系统5年演化史(代码片段)

作者介绍:杨凡,花名挽晴,饿了么高级架构师,2014年加入饿了么,2018年随饿了么被阿里巴巴收购一同加入阿里巴巴,4年团队管理经验,4年主要从事饿了么交易系统建设,也曾负责过饿了么账号... 查看详情

饿了么顶部搜索框下拉动画小效果

效果描述:当下拉的时候渐变产生对固定的搜索框importReact,{Component}from"react";import{connect}from"react-redux";import{Container,Header,Title,Content,Button,Icon,Left,Right,Body,FlatList,}from"native-base";import{Dimension 查看详情