精华内容
下载资源
问答
  • 最近项目中做图片拍照时遇到一个问题,当我将图片大小设置为固定值时,一些手机或者平板上面运行demo时就会出现相机初始化报错。后来通过网上查中一些例子和自己试验,最后总结了自己的一点心得。 1拍照图片...
       最近在项目中做图片拍照时遇到一个问题,当我将图片大小设置为固定值时,在一些手机或者平板上面运行demo时就会出现相机初始化报错。后来通过在网上查中一些例子和自己试验,最后总结了自己的一点心得。
    

    1在拍照图片预览和设置拍摄图片大小时,因为不同机型支持的图片长宽设置和预览长宽设置不一定全部相同,因此可能会导致程序奔溃。
    一下两张图片时我用一台华为平板和一台三星手机测试的结果
    华为平板结果
    这里写图片描述
    从结果中可以看到华为支持的照片组合只有17组;
    三星手机结果
    这里写图片描述
    三星支持的组合为31种,
    所以在设置相片大小时,为了适合在各个手机上面的兼容性,我们最好别将相片长宽设置为固定值。
    因此记录一下代码,方便日后重蹈覆辙。
    1 开启相机权限,文件读写权限

     <uses-permission android:name="android.permission.INTERNET"/>
        <uses-permission android:name="android.permission.CAMERA" />
        <!--在SDcard中创建与删除文件权限-->
        <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
        <!--往SDcard写入数据权限-->
        <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
        <uses-feature android:name="android.hardware.Camera"/>
    

    同时我将拍照设置为乐横屏

       <activity android:name=".MainActivity"
                android:screenOrientation="landscape"
                >
                <intent-filter>
                    <action android:name="android.intent.action.MAIN"
                        />
    
                    <category android:name="android.intent.category.LAUNCHER" />
                </intent-filter>
            </activity>

    2 layout 文件很简单,一个surfaceView,两个button。

       <?xml version="1.0" encoding="utf-8"?>
    <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context="com.camera.demo.MainActivity">
    
        <SurfaceView
            android:id="@+id/surface_view"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
    
        <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="vertical">
    
            <Button
                android:id="@+id/start"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_alignParentBottom="true"
                android:layout_alignParentLeft="true"
                android:layout_alignParentStart="true"
                android:layout_marginBottom="20dp"
                android:layout_marginLeft="20dp"
                android:layout_marginStart="20dp"
                android:padding="20dp"
                android:text="开始" />
    
            <Button
                android:id="@+id/btn_stop"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_alignParentRight="true"
                android:layout_alignParentEnd="true"
                android:layout_alignTop="@+id/start"
                android:layout_marginEnd="20dp"
                android:layout_marginRight="20dp"
                android:padding="20dp"
                android:text="停止" />
        </RelativeLayout>
    
    </FrameLayout>
    

    3摘要代码也很简单,也就是基本的相机拍照,
    MainActivity实现SurfaceHolder.Callback接口(开启相机,操作相机,关闭相机)和 Camera.PictureCallback (将拍摄的照片转换为字节)接口

    package com.camera.demo;
    
    
    import android.graphics.Bitmap;
    import android.graphics.BitmapFactory;
    import android.graphics.Matrix;
    import android.hardware.Camera;
    import android.os.Handler;
    import android.support.v7.app.AppCompatActivity;
    import android.os.Bundle;
    import android.util.Log;
    import android.view.SurfaceHolder;
    import android.view.SurfaceView;
    import android.view.View;
    import android.view.WindowManager;
    import android.widget.Button;
    
    import java.io.ByteArrayOutputStream;
    import java.util.List;
    
    
    public class MainActivity extends AppCompatActivity implements SurfaceHolder.Callback, View.OnClickListener, {
        private SurfaceView mSurficeView;
        private Button btnStart;
        private SurfaceHolder mHolder;
        private Camera camera;
        private Handler mHandler = new Handler();
        private boolean mIsSurfaceCreated = false;
        private static final int CAMERA_ID = 0; //选择后置摄像头
        private Button btnStop;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            initView();
            initEvent();
    
        }
    
        private void initView() {
            mSurficeView = (SurfaceView) findViewById(R.id.surface_view);
            btnStart = (Button) findViewById(R.id.start);
            btnStop = (Button) findViewById(R.id.btn_stop);
        }
    
        private void initEvent() {
            mHolder = mSurficeView.getHolder();
            mHolder.addCallback(this);
            btnStart.setOnClickListener(this);
            btnStop.setOnClickListener(this);
        }
    
        @Override
        protected void onPause() {
            super.onPause();
            stopPreview();
        }
    
    
        @Override
        public void surfaceCreated(SurfaceHolder holder) {
            mIsSurfaceCreated = true;
        }
    
        @Override
        public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
            Log.v("MainActivity", String.valueOf(width) + String.valueOf(height));
            //开启相机预览
            startPreview();
        }
    
        @Override
        public void surfaceDestroyed(SurfaceHolder holder) {
            mIsSurfaceCreated = false;
        }
    
        @Override
        public void onClick(View v) {
            switch (v.getId()) {
                case R.id.start:
                camera.startPreview();
               break;
                case R.id.btn_stop:
                 stopPreview()
                    break;
                default:
                    break;
            }
        }
    
    
        private void startPreview() {
            if (camera != null || !mIsSurfaceCreated) {
                return;
            }
            camera = Camera.open(CAMERA_ID);
            Camera.Parameters parameters = camera.getParameters();
            //设置连续对焦
            parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);
            //  设置相机预览方向
            camera.setDisplayOrientation(0);
            //获取手机支持的相片大小组合
            List<Camera.Size> picSize = camera.getParameters().getSupportedPictureSizes();
            int picLength = picSize.size();
            for (int i = 0; i < picLength; i++) {
                Camera.Size pSize = picSize.get(i);
                Log.v("MainActivity", "picWidth" + pSize.width + "pciHeight" + pSize.height);
            }
            //获取最小一组图片大小的长宽值
            int w = picSize.get(picSize.size() - 1).width;
            parameters.setPictureSize(picSize.get(picSize.size() - 1).width, picSize.get(picSize.size() - 1).height);
            //获取支持的图片预览大小
            List<Camera.Size> preSize = camera.getParameters().getSupportedPreviewSizes();
            for (int j = 0; j < preSize.size(); j++) {
                Camera.Size prSize = preSize.get(j);
                Log.v("MainActivity", "preWidth" + prSize.width + "preHeight" + prSize.height);
            }
            //获取最小一组图片预览长宽值
            parameters.setPreviewSize(preSize.get(preSize.size()-1).width,         preSize.get(preSize.size()-1).height);
            camera.setParameters(parameters);
            try {
                camera.setPreviewDisplay(mHolder);
            } catch (Exception e) {
                e.printStackTrace();
            }
            camera.startPreview();
        }
    
        private void stopPreview() {
            //释放camera
            if (camera != null) {
                try {
                    camera.setPreviewDisplay(null);
                    camera.stopPreview();
                    camera.release();
                    camera = null;
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    
        @Override
        protected void onDestroy() {
            super.onDestroy();
            camera = null;
        }
    }
    
      上面的代码中,我都是获取的最后的一组长宽值,项目中。我们可以根据需要去选取。但是最好别写固定值,之前在网上面看见很多都是写的固定值,走了不少弯路。。
    
    展开全文
  • 手机上如何将图片转换成PDF文档

    万次阅读 2018-09-04 19:02:27
    转换成PDF,大部分的人都会选择电脑完成这个操作,但其实手机上也可以完成, 这里小编就教大家简单的图片转换成PDF的方法。 使用工具;迅捷PDF阅读器 操作方法: 1、 打开手机在手机上下载安装迅捷PDF...

    我们在工作中经常遇会到的就是转换图片的格式,就比如像一些png、jpg等格式的图片

    转换成PDF,大部分的人都会选择在电脑上完成这个操作,但其实手机上也可以完成,

    在这里小编就教大家简单的图片转换成PDF的方法。

    这里写图片描述

    使用工具;迅捷PDF阅读器

    操作方法:

    1、 打开手机,在手机上下载安装迅捷PDF阅读器,然后再找到阅读器里面的小功能。

    这里写图片描述

    2、 点击“图片生成PDF”这一项功能,点击之后,先编辑要生成的PDF文档名称。

    这里写图片描述

    3、 然后再从手机相册里选择好要需要生成PDF格式的图片,选择好后点击确定。

    这里写图片描述

    4、 接着可以点击马上查看,然后就可以查看一下转换的结果。

    这里写图片描述

    上述内容就是将图片转换成PDF的全部步骤了,有需要转换图片格式的可以自己去手机上动手试试。

    展开全文
  • 今天就来说一说怎么去实现喽~(PS:咱们是讲原理和思路,我这里是用的不是360助手的界面,懒得去扒那些图片喽,太懒,见谅~~)首先,图: 先看一下工程目录结构: 其中有两个包,第一个里面分别是: Data:工程...

    转载请注明出处:http://blog.csdn.net/hello_chillax/article/details/45599215,谢谢~

    想必大家都用过360手机助手,效果是不是很绚丽,其实很好实现喽。今天就来说一说怎么去实现喽~(PS:咱们是讲原理和思路,我这里是用的不是360助手的界面,懒得去扒那些图片喽,太懒,见谅~~)

    首先,上图:
    这里写图片描述

    先看一下工程目录结构:
    这里写图片描述

    其中有两个包,第一个里面分别是:
    Data:工程里面的数据,本来想用几个textview填充ListView的,最后感觉这样会效果好一点,米啥用~
    MainActivity:不说了~
    Model:LIstView的每个item我都抽象出一个model,
    RollViewPager:大家也看到了,上面的那个ViewPager是会自己跳动的,为了实现这个效果,这里重写了ViewPager。

    另外一个包则是下拉刷新,上拉加载的ListView,是一个开源的东东~GitHub上有~

    这里重点说一下整个效果的思路:

    其实PullToRefreshListView内部维护了一个普通的ListView,并且实现了上拉加载,下拉刷新效果。然后我们在此基础之上加了一个自动翻滚的ViewPager,就实现了上面的效果。

    代码:

    MainActivity.java:

    //初始化View
    private void initView() {
            ptrLV = (PullToRefreshListView) findViewById(R.id.ptr_lv);
            // 滚动到底自动加载可用
            ptrLV.setPullRefreshEnabled(true);
            // 下拉刷新可用
            ptrLV.setPullLoadEnabled(false);
            // 滚动到底自动加载可用
            ptrLV.setScrollLoadEnabled(true);
            // 得到实际的ListView并设置监听器:
            ptrLV.getRefreshableView().setOnItemClickListener(
                    new OnItemClickListener() {
    
                        @Override
                        public void onItemClick(AdapterView<?> parent, View view,
                                int position, long id) {
                            Toast.makeText(MainActivity.this,
                                    "您点击的是第 " + position + " 个条目~",
                                    Toast.LENGTH_SHORT).show();
                        }
                    });
            // 自定义adapter:
            ptrLV.getRefreshableView().setAdapter(new MyAdapter());
            // 获取header的一个视图:
            topView = (ViewGroup) getLayoutInflater().inflate(
                    R.layout.layout_roll_view, null);
            // 设置圆点:
            dotsView = (LinearLayout) topView.findViewById(R.id.dots_ll);
            initDot(4);
            // 设置ListView的Header
            viewPager = new RollViewPager(this, dotList, R.drawable.dot_focus,
                    R.drawable.dot_normal, new OnPagerClickCallback() {
    
                        @Override
                        public void onPagerClick(int position) {
                            Toast.makeText(MainActivity.this,
                                    "您点击的是第 " + (position+1) + " 张图片~",
                                    Toast.LENGTH_SHORT).show();
                        }
                    });
    
            // 设置图片的路径
            int[] ids = new int[] { R.drawable.news_1, R.drawable.news_2,
                    R.drawable.news_3, R.drawable.news_4 };
            viewPager.setResImageIds(ids);
            // 设置图片的标题
            ArrayList<String> list = new ArrayList<String>();
            list.add("我是第1标题~~");
            list.add("我是第2标题~~");
            list.add("我是第3标题~~");
            list.add("我是第4标题~~");
            viewPager.setTitle(
                    (TextView) topView.findViewById(R.id.top_news_title), list);
            // 设置ViewPager开始转动
            viewPager.startRoll();
            pagerLy = (LinearLayout) topView.findViewById(R.id.top_news_viewpager);
            pagerLy.removeAllViews();
            pagerLy.addView(viewPager);
            // 添加header
            if (ptrLV.getRefreshableView().getHeaderViewsCount() < 1) {
                ptrLV.getRefreshableView().addHeaderView(topView);
            }
        }
    
        private void initDot(int size) {
            dotList = new ArrayList<View>();
            for (int i = 0; i < size; i++) {
                LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
                        10, 10);
                params.setMargins(5, 0, 5, 0);
                View dot = new View(this);
                if (i == 0) {
                    dot.setBackgroundResource(R.drawable.dot_focus);
                } else {
                    dot.setBackgroundResource(R.drawable.dot_normal);
                }
                dot.setLayoutParams(params);
                dotList.add(dot);
                dotsView.addView(dot);
            }
        }
    
        class MyAdapter extends BaseAdapter {
            @Override
            public int getCount() {
                return dataList.size();
            }
    
            @Override
            public Object getItem(int position) {
                return dataList.get(position);
            }
    
            @Override
            public long getItemId(int position) {
                return 0;
            }
    
            @Override
            public View getView(int position, View convertView, ViewGroup parent) {
                ViewHolder holder = new ViewHolder();
                if (convertView == null) {
                    convertView = getLayoutInflater().inflate(
                            R.layout.list_item_stytle, null);
                    convertView.setTag(holder);
                    holder.user_image = (ImageView) convertView
                            .findViewById(R.id.user_image);
                    holder.publish_user = (TextView) convertView
                            .findViewById(R.id.publish_user);
                    holder.publish_at = (TextView) convertView
                            .findViewById(R.id.publish_at);
                    holder.title = (TextView) convertView.findViewById(R.id.title);
                    holder.body = (TextView) convertView.findViewById(R.id.body);
                    holder.mark = (TextView) convertView.findViewById(R.id.mark);
                } else {
                    holder = (ViewHolder) convertView.getTag();
                }
                Model post = dataList.get(position);
                holder.user_image.setImageResource(post.image_path);
                holder.publish_user.setText(post.publish_user);
                holder.publish_at.setText(post.publish_at);
                holder.mark.setText(post.mark);
                holder.title.setText(post.title);
                holder.body.setText(post.body);
                return convertView;
            }
    
        }
    
        public final class ViewHolder {
            public ImageView user_image;
            public TextView publish_user;
            public TextView publish_at;
            public TextView mark;
            public TextView title;
            public TextView body;
        }

    主要的布局:

    <com.qianlong.android.view.pullrefreshview.PullToRefreshListView 
            android:id="@+id/ptr_lv"
            android:layout_width="match_parent"
            android:layout_height="match_parent"/>

    恩 简单~

    其实不难喽,还是不懂的可以参考本例的代码,我会给出Demo的下载地址

    Demo地址:http://download.csdn.net/detail/hello_chillax/8686921

    写得比较简单,不懂的地方请留言~

    展开全文
  • 本文内容:移动端实现图片拍摄、压缩、预览、裁剪、上传的五大功能,看起来是一套很复杂的业务逻辑组合,实际每个模块可以单独开发,细分并拆分业务模块是常见复杂业务形态开发的基本方案。 概述 移动端做...
    最新更新时间:2019年10月31日15:33:32

    《猛戳-查看我的博客地图-总有你意想不到的惊喜》

    本文内容:在移动端实现图片拍摄、压缩、预览、裁剪、上传的五大功能,看起来是一套很复杂的业务逻辑组合,实际上每个模块可以单独开发,细分并拆分业务模块是常见复杂业务形态开发的基本方案。

    概述

    在移动端做开发永远越不过的两个障碍或技术瓶颈,兼容性和性能。

    • 兼容性,某些HTML元素的默认样式在不同浏览器下显示效果不一;CSS样式的兼容性,移动端常见的是同一样式在不同OS和不同机型下显示效果不一;原生事件交互的兼容性,比如拍照和键盘输入场景下,Android和iOS系统表现的形式不一;
    • 性能,主要表现在硬件设备系统内存容量的受限,比如视频播放、图片连续拍摄都是高功耗的应用场景,处理不当容易造成内存泄漏导致浏览器crash。

    本文中的技术方案,瓶颈在于连续拍摄照片有数量限制,实测过程中iPhone X等高性能手机连续拍摄几十张照片的时候,容易导致浏览器crash,这个问题经过长期探索和研究,手动实现了实时垃圾回收,以及图片压缩比例调整和压缩时机控制,性能有所提高和改善,从二十张左右的数量提升到了四十张左右的数量,但部分机型无限连续拍摄图片出现崩溃的场景终究没有解决方案,究其本质原因,受限于手机系统、可用内存容量等硬件。

    本方案,对于大部分机型连续拍摄照片,并实施压缩预览裁剪上传功能,无数量限制。最终向服务器上传的是base64数据格式的图片数据。

    技术方案的实现

    • DOM布局如下:
    import React from 'react'
    //引入Cropper图片裁剪组件
    import Cropper from 'react-cropper';
    import 'cropperjs/dist/cropper.css';
    let styles = .contianer {
    	.cropModal{
    		position: absolute;
    		width: 100%;
    		height: 100%;
    		top: 0;
    		left: 0;
    		background: #000000;
    		display: flex;
    		flex-direction: column;
    		justify-content: center;
    		align-items: center;
    		z-index: 3;
    		.crop{
    		
    		}
    		.btn{
    			display: flex;
    			flex-direction: row;
    			justify-content: space-between;
    			position: absolute;
    			bottom: 0;
    			left: 50%;
    			transform: translateX(-50%);
    			width: 285px;
    			height: 60px;
    			.cropperBtn{
    				width: 60px;
    				height: 60px;
    				line-height: 30px;
    				color: #FFFFFF;
    				font-size: 14px;
    				text-align: center;
    				img{
    					width: 23px;
    					height: 22px;
    					vertical-align: top;
    					position: relative;
    					top: 50%;
    					transform: translateY(-50%);
    				}
    			}
    		}
    		.cropTips{
    			position: absolute;
    			top: 22px;
    			font-size: 11px;
    			line-height: 15px;
    			color: #B8B8B8;
    			padding: 0 26px;
    			letter-spacing: 0.5px;
    		}
    	}
    }
    
    export default class TakePhoto extends React.Component {
    
      constructor(props) {
        super(props);
        this.state = {
          displayLoading: false,
          cropperData:'',
          showCropModal: false
        };
        this.fReader = new FileReader();
        this.closureTime = 0;
      }
      
      render() {
        return <div id='testPage' className={styles.contianer}>
        {/*图片裁剪组件*/}
            {
              this.state.showCropModal ? <div className={styles.cropModal} id='cropModal'>
                <Cropper
                  className={styles.crop}
                  ref='cropper'
                  src={this.state.cropperData}
                  style={{maxHeight: '78%', width: '100%'}}
                  //0-默认-没有任何限制 1-限制裁剪框不超过canvas画布边缘 2-如果是长图-限制图片不超过cropper的最高可视区域-同时裁剪框不超过canvas画布边缘
                  viewMode={2}
                  dragMode='none'
                  minCanvasWidth={285}
                  //隐藏棋盘背景色
                  background={false}
                  //裁剪框内部的横竖虚线可见
                  guides={true}
                  //裁剪框内部的十字线可见
                  center={false}
                  //可旋转原图
                  rotatable={true}
                  //可缩放原图
                  scalable={true}
                  //crop={(e)=>{this.crop(e)}}
                />
                <div className={styles.btn}>
                  <div className={styles.cropperBtn} onClick={this.cancelCrop}>取消</div>
                  <div className={styles.cropperBtn} onClick={this.confirmCrop}>确认</div>
                  <div className={styles.cropperBtn} onClick={this.rotateCrop}>旋转</div>
                </div>
              </div> : null
            }
        	{this.state.displayLoading ? <Loading></Loading> : null}
    	    <input
    			type="file"
    			onChange={(e)=>{this.onChange(e)}}
    			className={styles.getImg}
    			title={this.state.title}
    			id="fileinput"
    			ref='onChange'
    			accept="image/*"
    			// capture="camera"
    		/>
        </div>
      }
    }
    
    • input元素onChange事件调起相机和相册的功能代码如下:
    /**
    * input onChange事件
    * @param e
    * @return
    */
    onChange(e){
    	//此处是崩溃点 相机调用的频率越高,崩溃越快
    	let _this = this;
    	//弹出加载动画
    	this.openLoading()
    	let file = e.currentTarget.files[0];//object-Blob //96K 的文件转换成 base64 是 130KB
    	//用户取消操作
    	if(file == undefined){
    		return
    	}
    	this.fReader = new FileReader();
    	let tempTimer = setTimeout(function(){
    		_this.fReader.readAsDataURL(file);
    		_this.fReader.onload=function(e) {
    			this.zip(this.result);//压缩逻辑
    		}
    		file = null;
    		tempTimer = null;
    	},500)
    }
    
    /**
    * 显示loading组件
    * @param
    * @return
    */
    openLoading(){
    	this.setState({
    		displayLoading: true
    	})
    }
    
    • 图片压缩
    /**
    * 图片压缩
    * @param base64
    * @return
    */
    zip(base64){
    	let img = new Image();
    	let canvas = document.createElement("canvas");
    	let ctx = canvas.getContext("2d");
    	let compressionRatio = 0.5
    	//获取用户拍摄图片的旋转角度
    	let orientation = this.getOrientation(this.base64ToArrayBuffer(base64));//1 0°  3 180°  6 90°  8 -90°
    	img.src = base64
    	img.onload = function () {
    		let width = img.width, height = img.height;
    		//图片旋转到 正向
    		if(orientation == 3){
    			canvas.width = width;
    			canvas.height = height;
    			ctx.rotate(Math.PI)
    			ctx.drawImage(img, -width, -height, width, height)
    		}else if(orientation == 6){
    			canvas.width = height;
    			canvas.height = width;
    			ctx.rotate(Math.PI / 2)
    			ctx.drawImage(img, 0, -height, width, height)
    		}else if(orientation == 8){
    			canvas.width = height;
    			canvas.height = width;
    			ctx.rotate(-Math.PI / 2)
    			ctx.drawImage(img, -width, 0, width, height)
    		}else{
    			//不旋转原图
    			canvas.width = width;
    			canvas.height = height;
    			ctx.drawImage(img, 0, 0, width, height);
    		}
    
    //第一次粗压缩
    // let base64 = canvas.toDataURL('image/jpeg', compressionRatio);//0.1-表示将原图10M变成1M 10-表示将原图1M变成10M
    //100保证图片容量 0.05保证不失真
    //console.log('第一次粗压缩',base64.length/1024,'kb,压缩率',compressionRatio);
    //第二次细压缩
    // while(base64.length/1024 > 500 && compressionRatio > 0.01){
    //console.log('while')
    // compressionRatio -= 0.01;
    // base64 = canvas.toDataURL('image/jpeg', compressionRatio);//0.1-表示将原图10M变成1M 10-表示将原图1M变成10M
    //console.log('第二次细压缩',base64.length/1024,'kb,压缩率',compressionRatio)
    // }
    		this.setCropperDate(canvas.toDataURL('image/jpeg', compressionRatio));
    	};
    }
    
    /**
    * 拍照第一次压缩后为cropper组件赋值
    * @param imgDataBase64 图片的base64
    * @return
    */
    setCropperDate = (imgDataBase64) => {
    	let _this = this;
    	this.state.cropperData = imgDataBase64;
    	//定时器的作用,上面的imgDataBase64赋值,属于大数据赋值操作,消耗资源过大,加上定时器等待大数据赋值成功内存释放之后再渲染UI,不会出现白屏
    	let tempTimer = setTimeout(function(){
    		_this.setState({
    			displayLoading: false,
    			showCropModal: true
    		})
    		clearTimeout(tempTimer)
    	},300)
    }
    
    • 获取图片的旋转角度
    /**
    * base64转ArrayBuffer对象
    * @param base64
    * @return buffer
    */
    base64ToArrayBuffer(base64) {
    	base64 = base64.replace(/^data\:([^\;]+)\;base64,/gmi, '');
    	var binary = atob(base64);
    	var len = binary.length;
    	var buffer = new ArrayBuffer(len);
    	var view = new Uint8Array(buffer);
    	for (var i = 0; i < len; i++) {
    		view[i] = binary.charCodeAt(i);
    	}
    	return buffer;
    }
    	
    /**
    * 获取jpg图片的exif的角度
    * @param
    * @return
    */
    getOrientation(arrayBuffer) {
    	var dataView = new DataView(arrayBuffer);
    	var length = dataView.byteLength;
    	var orientation;
    	var exifIDCode;
    	var tiffOffset;
    	var firstIFDOffset;
    	var littleEndian;
    	var endianness;
    	var app1Start;
    	var ifdStart;
    	var offset;
    	var i;
    	// Only handle JPEG image (start by 0xFFD8)
    	if (dataView.getUint8(0) === 0xFF && dataView.getUint8(1) === 0xD8) {
    		offset = 2;
    		while (offset < length) {
    			if (dataView.getUint8(offset) === 0xFF && dataView.getUint8(offset + 1) === 0xE1) 
    				app1Start = offset;
    				break;
    			}
    			offset++;
    		}
    	}
    	if (app1Start) {
    		exifIDCode = app1Start + 4;
    		tiffOffset = app1Start + 10;
    		if (getStringFromCharCode(dataView, exifIDCode, 4) === 'Exif') {
    			endianness = dataView.getUint16(tiffOffset);
    			littleEndian = endianness === 0x4949;
    			if (littleEndian || endianness === 0x4D4D /* bigEndian */) {
    				if (dataView.getUint16(tiffOffset + 2, littleEndian) === 0x002A) {
    					firstIFDOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
    					if (firstIFDOffset >= 0x00000008) {
    						ifdStart = tiffOffset + firstIFDOffset;
    					}
    				}
    			}
    		}
    	}
    	if (ifdStart) {
    		length = dataView.getUint16(ifdStart, littleEndian);
    		for (i = 0; i < length; i++) {
    			offset = ifdStart + i * 12 + 2;
    			if (dataView.getUint16(offset, littleEndian) === 0x0112 /* Orientation */) {
    				// 8 is the offset of the current tag's value
    				offset += 8;
    				// Get the original orientation value
    				orientation = dataView.getUint16(offset, littleEndian);
    				// Override the orientation with its default value for Safari (#120)
    				if (true) {
    					dataView.setUint16(offset, 1, littleEndian);
    				}
    				break;
    			}
    		}
    	}
    	return orientation;
    }
    	
    /**
    * Unicode码转字符串  ArrayBuffer对象 Unicode码转字符串
    * @param
    * @return
    */
    getStringFromCharCode(dataView, start, length) {
    	var str = '';
    	var i;
    	for (i = start, length += start; i < length; i++) {
    		str += String.fromCharCode(dataView.getUint8(i));
    	}
    	return str;
    }
    
    • Cropper组件的取消、裁剪、旋转的三个方法:
    /**
    * 无线逆时针旋转图片
    * @param
    * @return
    */
    rotateCrop(){
    	this.refs.cropper.rotate(-90);
    }
    
    /**
    * 在裁剪组件中确认裁剪
    * @param
    * @return
    */
    confirmCrop(){
    	let _this = this;
    	//节流
    	if(Date.now() - this.closureTime < 2000){
    		return
    	}
    	this.closureTime = Date.now()
    	document.getElementById('cropModal').style.visibility = 'hidden';
    	this.setState({
    		displayLoading: true,
    	})
    	let tempTimer = setTimeout(function(){
    		//获取裁剪后的图片base64 向服务器传递500KB以内的图片
    		let compressionRatio = 0.5;
    		let cropperData = _this.refs.cropper.getCroppedCanvas().toDataURL("image/jpeg", compressionRatio)
    		while(cropperData.length/1024 > 500 && compressionRatio > 0.1){
    			compressionRatio -= 0.1;
    			cropperData = _this.refs.cropper.getCroppedCanvas().toDataURL("image/jpeg", compressionRatio)
    		}
    		_this.state.cropperData = null;
    		_this.refs.cropper.clear();//去除裁剪框
    		//_this.refs.cropper.destroy();//需要修改npm包
    		_this.upload(cropperData);//向服务器提交base64图片数据
    		cropperData = null;
    		//必须先拿到cropper数据 关闭裁剪框 显示加载框
    		_this.setState({showCropModal: false})
    		clearTimeout(tempTimer)
    	},300)
    }
    
    /**
    * 在裁剪组件中取消裁剪
    * @param
    * @return
    */
    cancelCrop(){
    	this.state.cropperData = null;
    	this.refs.cropper.clear()
    	this.setState({
    		showCropModal: false
    	})
    }
    

    参考资料

    感谢阅读,欢迎评论^-^

    打赏我吧^-^

    展开全文
  • 我想做一个html导航 用js控制 小图表示自己 大图是地图,是浮动的图,能用触屏放大缩小 小图要根据与地图像数比例同步放大...小图根据x,y叠加地图对应的位置 无论地图怎样变,小图相对地图位置不变
  • 手机九宫格解锁图案如上,假设把一次先行后列标记九个圆一次为1,2,3,4,5,6,7,8,9这九个数字,求所有合法的密码情况 合法的密码我们假设要求如下: 1、假设密码的长度至少为2,最长当然为9 2、密码中不能...
  • 手机图片、铃声免费下载终极大法

    千次阅读 2005-07-18 17:32:00
    如今手机的功能越来越强大,26万色显示屏,64和弦,红外线连接……这些功能令手机DIYER们兴奋不已。下载最新的动画屏保、最酷的铃声,大概就是最热衷的事情... 使用下面介绍的方法之前,您需要作以下准备:电脑
  • 最近公司要求做一个类似腾讯手机管家的应用,其中有一个功能需要把用户手机中的相似图片查找出来,供用户选择是否删除。 完全毫无头绪,就上网查找有哪些技术可以做的,找来找去,都说OpenCV,于是乎就看看OpenCV...
  • a-手指滑动页角到达的位置b-当前页翻起来后与书本垂直边的交点c-当前页翻起来后与书本水平边的交点m-翻页的起始点n-书本右角t-书本左上角o-直角坐标系原点为了能翻页的过程中看到下一页的部分内容,每次翻页...
  • 根据电话的按键将数字转换成对应的可能字母组合 下面为图片样例 图片: 示例: 输入:“34” 输出:[“dg”,“dh”,“di”,“eg”,“eh”,“ei”,“fg”,“fh”,“fi”] js解决方案: //定义好确定的资源变量 var ...
  • 1.客户端模拟图片上传程序(test.php): $data=file_get_contents('1.png'); $http_entity_body = $data; $http_entity_type = 'application/x-www-form-urlencoded'; $http_entity_length = st
  • 效果说明:一排图片大小一致,当鼠标放在任一图片上时,图片放大并且旋转。 制作思路: 1、给图片添加&lt;a&gt;标签,利用伪类选择器实现 2、设置鼠标悬浮a标签上时,添加动画属性。 3、主要用到属性...
  • title: adb命令截图图片显示为黑屏问题总结 date: 2020-01-07 tags: adb android author: YeXiaoXin 前言 本文主要介绍 Xpath 定位的语句语法,应用场景为Web、APP UI自动化的元素定位中。 ...
  • 一些术语 Screen Size 屏幕尺寸: 实际的物理尺寸,以屏幕的对角线为准(包括通知栏?) 将所有的实际尺寸分为四个广义的尺寸:small(小),...small的屏幕密度比normal或large一个给定的物理面积内具有较少的像
  • 项目中用户需要让我把二维码和背景图保存到一张图片上,其实大家都知道,这个可以用canvas来实现。简单上网搜了一下,调试了下,终于成功了,写篇日志记录下源码 <template> <div class="content">...
  • Android自定义控件View(三)组合控件

    千次阅读 2015-05-30 09:49:34
    不少人应该见过小米手机系统音量控制UI,一个圆形带动画效果的音量加减UI,效果很好看。它是怎么实现的呢?这篇博客来揭开它的神秘面纱。先效果图 相信很多人都知道Android自定义控件的三种方式,Android自定义...
  • 手机软件工作原理

    千次阅读 2012-05-29 14:27:10
    手机的雏形十分类似于对讲机,最早出现20世纪40年代,曾第二次世界大战用于军事通话,是后来的“大哥大”的前身。哪个时候还没有手机软件的概念,手机上也没有任何增值的服务。后来手机逐渐从军用转为商用`民用...
  • 前面四篇文章的当中,我们已经学习了Glide的基本用法、Glide的工作原理和执行流程、Glide的缓存机制、以及Glide的回调机制等内容。如果你能将前面的四篇文章都掌握好了,那么恭喜你,现在你已经是一名Glide好手了...
  • 【本文为囧希博客个人原创文章,转载请注明出处!... 虚拟增强现实技术是当下最具有想象力的移动应用方向之一,通过移动设备扫描对应的图片(不限于二维码),可以将特定图片(室外广告,...【实际看过本文的教程确实可
  • 设计模式练习(8)——组合模式

    千次阅读 2017-01-20 15:26:41
    组合模式 一、题目:使用组合模式,设计一个杀毒软件(AntiVirus)的框架,该软件既可以对某个文件夹Folder杀毒,也可以对某个指定的文件杀毒,文件种类包括,文本文档TextFile,图片文件ImageFile,视频文件VideoFile...
  • 智能手机上的常用传感器

    万次阅读 2013-10-15 18:09:18
    手机上有很多的Sensor,具体做什么还很模糊,找了很多资料,就这篇还算全。 =================================================================================================================================...
  • 1,本站注册的推广分销人员需要有自己的独有邀请码,这个邀请码需要转换成二维码,并让推广员保存在手机相册中,分发到其它群中,进行二维码图片推广 2,但是单独二维码过于简陋, 不够吸引眼球.需要加一个好看的背景. 3,...
  • android中画圆形图片的几种办法

    千次阅读 2014-09-19 22:33:29
    开发中经常会有一些需求,比如显示头像,显示一些特殊的需求,将图片显示成圆角或者圆形或者其他的一些形状。但是往往我们手图片或者从服务器获取到的图片都是方形的。这时候就需要我们自己进行处理,将图片处理...
  • iOS苹果手机上最好的3个azw3阅读器

    千次阅读 2020-07-28 13:59:28
    今天小编就为大家推荐3个安卓手机上最好的azw3阅读器。 第一款:neat reader 这款阅读器界面设计相当整洁,色调以淡蓝色和白色为主,整体阅读视觉感舒适,阅读的基础功能,书签,高亮,注释这些阅读时...
  • 所以,精美的图片可以说是必不可少的一部分,不仅阅读时能带来视觉的冲击,而且图片相比文字能涵盖更多的信息,不然怎会有一图胜千言的说法呢? 这时,可能有的读者会说自己不写文章呀,是不是没有必要了解
  • 手机上播放动画教程(一)

    千次阅读 2002-05-13 09:39:00
    一、概述 想在手机上开发丰富多采的动画效果吗?这本书将向你介绍各种能被Plazmic Media Engine1.0.3(针对java手机而开发的PME动画浏览器)支持的SVG元素.这一章节分为以下部分: 1.什么是"well-formed"SVG?2.如何...
  • 联想的投影手机可投射触屏到任何表面 内置投影仪的智能机--好吧,真的只有两款三星出品的--到目前为止都没有真正大受欢迎,但是联想认为它最终找到了这种组合正确的用例:投射触屏或者虚拟键盘到你的桌面。今天...
  • 本文不是神经网络或机器学习的入门教学,而是通过一个真实的产品案例,展示了在手机客户端运行一个神经网络的关键技术点 卷积神经网络适用的领域里,已经出现了一些很经典的图像分类网络,比如 VGG16/VGG19,...
  • Android控件组合应用一

    万次阅读 热门讨论 2011-05-16 17:12:00
    通过仿新浪微博界面的练习,讲解Android控件的组合应用
  •  篇文章地址:支付宝手机网页即时到账接口(1)之相关信息介绍。  这是篇文章结尾调用授权接口的图解。  官方文档中对授权接口的说明是: 商户利用编程方法来模拟http请求远程解析html,获取支付宝返回的...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 41,881
精华内容 16,752
关键字:

如何在手机上组合图片