草庐IT

微信小程序调用相机选择图片生成水印

随遇而安sd^_^ 2023-04-22 原文

项目近期有一个需求,是在小程序使用相机拍照时,页面缩略图和上传服务器(或保存本地)的都是带水印的,水印文案是当前的时间和当前所处的地点。

前期使用了微信小程序的wx.chooseImage相机拍照,再使用拍摄后的图片添加水印,上传或者保存本地,这样做发现,每保存一次,相册里面都添加了两张图片,测试后发现,wx.chooseImage相机拍照默认保存到相册了,这样的话就会有问题,后期就改成了自己调用相机上传图片,发现是可以实现的,代码如下:

camera.wxml:

<view class="cameraWrapper" hidden="{{ markPhoto }}">
  <view class='camera'>
    <camera wx:if="{{isAuth}}" device-position="back" flash="off" binderror="error"></camera>
  </view>
  <view class="btn-group">
    <mp-icon class="iconBtn returnBtn" icon="previous" color="#666" size="34" bindtap="returnCarmera"></mp-icon>
    <button class="takePhoto" type="primary" plain="{{true}}" bindtap="camera"><text></text></button>
  </view>
  <!-- 添加水印 -->
	<view class="canvas-cont">
		<canvas canvas-id='firstCanvas' style="width: {{w}}px;height: {{h}}px;"></canvas>
	</view>
</view>

<!-- 拍摄后生成水印图片 -->
<view class="camera-cont" hidden="{{ !markPhoto }}">
	<view class="preview-image-cont">
		<image class="preview-image" src="{{markPhoto}}" mode="aspectFit"></image>
	</view>
	<view class="btn-group btn-group-padding" style="{{ markPhoto=='' ? 'display:none' : ''}}">
		<mp-icon class="iconBtn" icon="previous" color="#666" size="44" bindtap="againBtn"></mp-icon>
		<icon class="icon-box-img" type="success" size="60" bindtap="saveBtn"></icon>
	</view>
</view>

camera.js:

const util = require('../../../utils/util')
const app = getApp()
Page({
	data: {
		isAuth: false,
		markPhoto: null,
		prePage: null // 从哪个页面进入
	},
	onLoad: function (options) {
		this.setData({ prePage: options.page })
		wx.showLoading({ title: "正在加载中...", mask: true })
		app.globalData.pageName = this // 将globalData的页面指向自己
		const that = this
		wx.getSetting({
			success: res => {
				if (res.authSetting['scope.camera']) {
					// 用户已经授权
					wx.hideLoading()
					that.setData({ isAuth: true })
				} else {
					// 用户还没有授权,向用户发起授权请求
					wx.authorize({
						scope: 'scope.camera',
						success() { // 用户同意授权
							wx.hideLoading()
							that.setData({ isAuth: true })
						},
						fail() { // 用户不同意授权
							that.openSetting().then(res => {
								wx.hideLoading()
								that.setData({ isAuth: true })
							})
						}
					})
				}
			},
			fail: res => {
				wx.hideLoading()
				console.log('获取用户授权信息失败')
			}
		})
	},
	// 打开授权设置界面
	openSetting: function () {
		const that = this
		let promise = new Promise((resolve, reject) => {
		wx.showModal({
		  title: '授权',
		  content: '请先授权获取摄像头权限',
		  success(res) {
			if (res.confirm) {
			  wx.openSetting({
				success(res) {
				  if (res.authSetting['scope.camera']) { // 用户打开了授权开关
					resolve(true)
				  } else { // 用户没有打开授权开关, 继续打开设置页面
					that.openSetting().then(res => { resolve(true) })
				  }
				},
				fail(res) {
				  console.log(res)
				}
			  })
			} else if (res.cancel) {
			  that.openSetting().then(res => { resolve(true) })
			}
		  }
	  })
	  })
	  return promise;
	},
  	// 服务站端-水印相机-调用摄像头拍照
  	camera: function () {
		const that = this
		if (app.globalData.address) {
			const ctx = wx.createCameraContext()
			ctx.takePhoto({
				quality: 'normal',
				success: (res) => {
					// console.log('res拍照', res)
					wx.showLoading({ title: "正在加载图片...", mask: true })
					that.addMark(res.tempImagePath)
				},
				fail (error) {
					wx.showToast({ title: error.errMsg, icon: 'none', duration: 2000 })
					setTimeout( () => { wx.navigateBack() }, 2000)
				}
			})
		} else {
			wx.showModal({ 
				title: '提示', content: '请先授权获取当前地理位置',
				success (res) {
					if (res.confirm) {
						app.getLocation('again')
					} else if (res.cancel) {
						wx.navigateBack()
					}
				}
			})
		}
	},
	// 获取图片信息
	addMark: function (file) {
		const that = this
		wx.getImageInfo({
			src: file,
			success(res2) {
				that.getCanvasImg(res2)
			}
		})
	},
  	// 服务站端-相机-canvas添加水印
	getCanvasImg: function (imgInfo) {
		wx.showLoading({ title: "图片努力生成中...", mask: true })
		const that = this
		const today = util.formatTime(new Date())
		const addressTxt = app.globalData.address
		let { path, width, height } = imgInfo
		that.setData({ w: width, h: height }) // 720 1206
		// 创建canvas
		const ctx = wx.createCanvasContext('firstCanvas', that)
		ctx.drawImage(path, 0, 0, width, height) // 先画出图片 地址,在canvas上X轴的位置,在canvas上y轴的位置,图片的宽度,图片的高度
		let fontSize = 30
		let rectY = height - 140
		let rectH = 140
		let imgWidth = 100
		let timeY = height - 80
		let addressY = height - 40
		let logoX = width - 120
		let logoY = height - 120
		let txtMaxWidth = width - imgWidth - 80
		if (addressTxt.length > 20) {
			rectY = height - 180
			rectH = 180
			imgWidth = 140
			timeY = height - 120
			addressY = height - 76
			logoX = width - 160
			logoY = height - 160
			txtMaxWidth = width - imgWidth - 80
		}
		ctx.setFontSize(fontSize) //注意:设置文字大小必须放在填充文字之前,否则不生效
		ctx.setFillStyle('rgba(0, 0, 0, .3)')
		ctx.fillRect(0, rectY, width, rectH)
		ctx.drawImage('/assets/images/logo.png', logoX, logoY, imgWidth, imgWidth)
		ctx.setFillStyle('rgba(255, 255, 255, 1)')
		ctx.fillText(today, 30, timeY)
		if (addressTxt.length < 20) {
			ctx.fillText(addressTxt, 30, addressY)
		} else {
			var chr = addressTxt.split("");//这个方法是将一个字符串分割成字符串数组
			var temp = "";
			var row = [];
			for (var a = 0; a < chr.length; a++) {
				if (ctx.measureText(temp).width < txtMaxWidth) {
					temp += chr[a];
				} else {
					a--; //这里添加了a-- 是为了防止字符丢失,效果图中有对比
					row.push(temp);
					temp = "";
				}
			}
			row.push(temp); 
		
			// 如果数组长度大于2 则截取前两个
			if (row.length > 2) {
				var rowCut = row.slice(0, 2);
				var rowPart = rowCut[1];
				var test = "";
				var empty = [];
				for (var a = 0; a < rowPart.length; a++) {
					if (ctx.measureText(test).width < 460) {
						test += rowPart[a];
					} else {
						break;
					}
				}
				empty.push(test);
				var group = empty[0] + "..." // 这里只显示两行,超出的用...表示
				rowCut.splice(1, 1, group);
				row = rowCut;
			}
			for (var b = 0; b < row.length; b++) {
				ctx.fillText(row[b], 30, addressY + b * 40, txtMaxWidth);
			}
		}
		ctx.draw(false, (() => {
			setTimeout( () => {
				// 生成图片把当前画布指定区域的内容导出生成指定大小的图片。在 draw() 回调里调用该方法才能保证图片导出成功
				wx.canvasToTempFilePath({
					quality: 0.5,
					fileType: 'jpg',
					canvasId: 'firstCanvas',
					success: function (res) {
						wx.hideLoading()
						that.setData({ 'markPhoto': res.tempFilePath })
					},
					fail: function(error) {
						wx.hideLoading()
						wx.showToast({ title: error.errMsg, icon: 'none', duration: 2000 })
					}
				}, that)
			}, 100)
		})())
	},
	// 重拍
	againBtn: function () {
		this.setData({ 'markPhoto': null })
	},
	// 保存图片到相册
	saveBtn: function () {
		const that = this
		if (that.data.prePage == 'complete') {
			let pages = getCurrentPages() // 获取当前页面
			let prePage = pages[pages.length - 2] // 获取上一页面
			prePage.setData({
				'markPhoto': that.data.markPhoto     //给上一页面的变量赋值
			})
			prePage.uploadMark(that.data.markPhoto) // 调用上一页面的方法(加载数据)
			wx.navigateBack({ delta: 1 }) // 返回上一页面
		} else {
			wx.saveImageToPhotosAlbum({ // 保存图片到系统相册
				filePath: that.data.markPhoto,
				success(res) {
					that.setData({ 'markPhoto': null })
				}
			})
		}
	},
	// 相机返回
	returnCarmera: function () {
		wx.navigateBack()
	}
})

camera.json:

{
    "component": true,
    "usingComponents": {
      "mp-icon": "weui-miniprogram/icon/icon"
    },
    "enablePullDownRefresh": true
  }

camera.wxss:


/* 模拟相机 */
.cameraWrapper {
    width: 100vw;
    height: 100vh;
    display: flex;
    flex-direction: column;
}
.camera {
    flex: 1;
    width: 100%;
    position: relative;
}
.camera camera {
    width: 100vw;
    height: calc(100vh - 200rpx);
    box-sizing: border-box;
}
.btn-group {
    position: relative;
    height: 200rpx;
    background-color: #fff;
    display: flex;
    align-items: center;
    justify-content: space-around;
}
.btn-group button.takePhoto:not([size='mini']) {
    position: relative;
    width: 120rpx;
    height: 120rpx;
    border-radius: 50%;
    border-width: 4rpx;
}
.btn-group button.takePhoto:not([size='mini']) text {
    position: absolute;
    top: 0;
    bottom: 0;
    left: 0;
    right: 0;
    width: 90rpx;
    height: 90rpx;
    border-radius: 50%;
    margin: auto;
    background-color: #07c160;
}
.btn-group .iconBtn {
    border: 4rpx solid #666;
    border-radius: 50%;
}
.btn-group .returnBtn {
    position: absolute;
    bottom: 19%;
    left: 14%;
}
.cameraWrapper .canvas-cont {
    width: 0px;
    height: 0px;
    position: fixed;
    left: 90000000px;
    z-index: -999;
    overflow: hidden;
}

/* 拍摄后生成水印图片 */
.camera-cont {
    width: 100vw;
    height: 100vh;
}
.camera-cont .preview-image-cont {
    height: calc(100% - 200rpx);
    width: 100%;
    display: flex;
    align-items: center;
    justify-content: center;
}
.camera-cont .preview-image {
    width: 100%;
    height: 100%;
}
.camera-cont .btn-group-padding {
    padding: 0 12%;
}
.camera-cont .icon-box-img {
    margin-top: -4rpx;
}

let pages = getCurrentPages() // 获取当前页面
let prePage = pages[pages.length - 2] // 获取上一页面
prePage.setData({‘markPhoto’: that.data.markPhoto}) //给上一页面的变量赋值
prePage.uploadMark(that.data.markPhoto) // 调用上一页面的方法(加载数据)
– prePage.uploadMark()是调用上一个页面的方法

// 页面调用子组件的方法
uploadMark: function (markPhoto) {
this.selectComponent(‘#uploadMarkId’).handleUpload(markPhoto)
},
uploadMarkChoose: function (pictureSource) {
this.selectComponent(‘#uploadMarkId’).chooseImage(pictureSource)
}

有关微信小程序调用相机选择图片生成水印的更多相关文章

  1. ruby - 使用 RubyZip 生成 ZIP 文件时设置压缩级别 - 2

    我有一个Ruby程序,它使用rubyzip压缩XML文件的目录树。gem。我的问题是文件开始变得很重,我想提高压缩级别,因为压缩时间不是问题。我在rubyzipdocumentation中找不到一种为创建的ZIP文件指定压缩级别的方法。有人知道如何更改此设置吗?是否有另一个允许指定压缩级别的Ruby库? 最佳答案 这是我通过查看ruby​​zip内部创建的代码。level=Zlib::BEST_COMPRESSIONZip::ZipOutputStream.open(zip_file)do|zip|Dir.glob("**/*")d

  2. ruby - 在 jRuby 中使用 'fork' 生成进程的替代方案? - 2

    在MRIRuby中我可以这样做:deftransferinternal_server=self.init_serverpid=forkdointernal_server.runend#Maketheserverprocessrunindependently.Process.detach(pid)internal_client=self.init_client#Dootherstuffwithconnectingtointernal_server...internal_client.post('somedata')ensure#KillserverProcess.kill('KILL',

  3. ruby - 如何使用 Ruby aws/s3 Gem 生成安全 URL 以从 s3 下载文件 - 2

    我正在编写一个小脚本来定位aws存储桶中的特定文件,并创建一个临时验证的url以发送给同事。(理想情况下,这将创建类似于在控制台上右键单击存储桶中的文件并复制链接地址的结果)。我研究过回形针,它似乎不符合这个标准,但我可能只是不知道它的全部功能。我尝试了以下方法:defauthenticated_url(file_name,bucket)AWS::S3::S3Object.url_for(file_name,bucket,:secure=>true,:expires=>20*60)end产生这种类型的结果:...-1.amazonaws.com/file_path/file.zip.A

  4. 使用 ACL 调用 upload_file 时出现 Ruby S3 "Access Denied"错误 - 2

    我正在尝试编写一个将文件上传到AWS并公开该文件的Ruby脚本。我做了以下事情:s3=Aws::S3::Resource.new(credentials:Aws::Credentials.new(KEY,SECRET),region:'us-west-2')obj=s3.bucket('stg-db').object('key')obj.upload_file(filename)这似乎工作正常,除了该文件不是公开可用的,而且我无法获得它的公共(public)URL。但是当我登录到S3时,我可以正常查看我的文件。为了使其公开可用,我将最后一行更改为obj.upload_file(file

  5. c# - 如何在 ruby​​ 中调用 C# dll? - 2

    如何在ruby​​中调用C#dll? 最佳答案 我能想到几种可能性:为您的DLL编写(或找人编写)一个COM包装器,如果它还没有,则使用Ruby的WIN32OLE库来调用它;看看RubyCLR,其中一位作者是JohnLam,他继续在Microsoft从事IronRuby方面的工作。(估计不会再维护了,可能不支持.Net2.0以上的版本);正如其他地方已经提到的,看看使用IronRuby,如果这是您的技术选择。有一个主题是here.请注意,最后一篇文章实际上来自JohnLam(看起来像是2009年3月),他似乎很自在地断言RubyCL

  6. ruby-on-rails - Ruby on Rails - 为文本区域和图片生成列 - 2

    我是Rails的新手,所以请原谅简单的问题。我正在为一家公司创建一个网站。那家公司想在网站上展示它的客户。我想让客户自己管理这个。我正在为“客户”生成一个表格,我想要的三列是:公司名称、公司描述和Logo。对于名称,我使用的是name:string但不确定如何在脚本/生成脚手架终端命令中最好地创建描述列(因为我打算将其设置为文本区域)和图片。我怀疑描述(我想成为一个文本区域)应该仍然是描述:字符串,然后以实际形式进行调整。不确定如何处理图片字段。那么……说来话长:我在脚手架命令中输入什么来生成描述和图片列? 最佳答案 对于“文本”数

  7. java - 从 JRuby 调用 Java 类的问题 - 2

    我正在尝试使用boilerpipe来自JRuby。我看过guide从JRuby调用Java,并成功地将它与另一个Java包一起使用,但无法弄清楚为什么同样的东西不能用于boilerpipe。我正在尝试基本上从JRuby中执行与此Java等效的操作:URLurl=newURL("http://www.example.com/some-location/index.html");Stringtext=ArticleExtractor.INSTANCE.getText(url);在JRuby中试过这个:require'java'url=java.net.URL.new("http://www

  8. ruby - 调用其他方法的 TDD 方法的正确方法 - 2

    我需要一些关于TDD概念的帮助。假设我有以下代码defexecute(command)casecommandwhen"c"create_new_characterwhen"i"display_inventoryendenddefcreate_new_character#dostufftocreatenewcharacterenddefdisplay_inventory#dostufftodisplayinventoryend现在我不确定要为什么编写单元测试。如果我为execute方法编写单元测试,那不是几乎涵盖了我对create_new_character和display_invent

  9. ruby-on-rails - 如何生成传递一些自定义参数的 `link_to` URL? - 2

    我正在使用RubyonRails3.0.9,我想生成一个传递一些自定义参数的link_toURL。也就是说,有一个articles_path(www.my_web_site_name.com/articles)我想生成如下内容:link_to'Samplelinktitle',...#HereIshouldimplementthecode#=>'http://www.my_web_site_name.com/articles?param1=value1¶m2=value2&...我如何编写link_to语句“alàRubyonRailsWay”以实现该目的?如果我想通过传递一些

  10. ruby - Rails 3 的 RGB 颜色选择器 - 2

    状态:我正在构建一个应用程序,其中需要一个可供用户选择颜色的字段,该字段将包含RGB颜色代码字符串。我已经测试了一个看起来很漂亮但效果不佳的。它是“挑剔的颜色”,并托管在此存储库中:https://github.com/Astorsoft/picky-color.在这里我打开一个关于它的一些问题的问题。问题:请建议我在Rails3应用程序中使用一些颜色选择器。 最佳答案 也许页面上的列表jQueryUIDevelopment:ColorPicker为您提供开箱即用的产品。原因是jQuery现在包含在Rails3应用程序中,因此使用基

随机推荐