• 关于 创建canvas 的搜索结果

回答

"这个只能通过最新的HTML5的标准还有各浏览器的支持情况 网上有一个HTML5标准是否支持某一个元素或功能的网站 找找 我之前看过###### 用Javascript可以检测,思路是这样的: 在页面创建某个标记(语法正确时总能创建,即使不能被解释),然后检测这个标记的HTML5特有属性特征(不能与HTML4重叠的,否则无法判断了),如果检测到就算有。 比如说创建一个canvas,再检测这个canvas标签是否具有getContext()方法(或属性),因为这个方法不是你手动添加的,而是当浏览器核心具有这种解释能力的时候内部执行构造函数时就具有的,所以就能检测到了。 这里只是一个思路,掌握越多的特征,做的越细致越能检测精确。   当然,还有一个更简单的方法,直接检测浏览器的userAgent,针对已知的浏览器解释能力列表也可以快速检测……人品爆发的时候,比如说遇到IE6了,基本就不用检测了   这里附上一段 http://www.one-lab.net/index.htm的检测canvas的示范代码 function try_canvas() { var canvas = document.createElement("canvas"); var div = document.getElementById("canvas_div"); if (canvas) { if (canvas.getContext) { var context = canvas.getContext("2d"); if (context) { context.globalCompositeOperation = "copy"; canvas.setAttribute("title", "ONE Browser Demo UI"); canvas.setAttribute("id", "canvas_body"); canvas.setAttribute("width", 700); canvas.setAttribute("height", 500); canvas.oncontextmenu = new Function("event.returnValue = false;"); div.appendChild(canvas); setInterval("canvas_animate()", imgAnimationDelay); document.getElementById("canvas_div_inner").style.display = "none"; } } else { document.getElementById("canvas_div_inner").style.display = "block"; } } } ######参考这里吧: http://www.w3cschool.cn/html5_reference.html

kun坤 2020-05-26 11:12:23 0 浏览量 回答数 0

问题

为什么Tkinter图像不显示,如果创建在一个函数?

kun坤 2019-12-25 21:49:08 3 浏览量 回答数 1

问题

将TouchListener添加到Drawable

LiuWH 2020-01-24 16:36:32 0 浏览量 回答数 1

企业特惠专场

万券齐发助力企业上云,低至2.2折起

回答

随便浏览了一下百度的源码(那个在homeView.js里的),大致实现的过程是:1.先看看浏览器支持Canvas不支持,如果支持就创建一个Canvas元素,然后画上几百个点,不支持就创建一个DIV往里扔几百个img元素,创建的时候根据规则设置了点到观察者的假想距离(影响后面的移动速度计算),并且点有大有小;2.监视mousemove事件,不停地更新一个鼠标位置对象,此对象用于后面的计算;3.搞一个1000/30毫秒周期的计时器,每个周期内计算各点的当前坐标,并重画Canvas或者调整img元素的位置;4.每个点的目标位置通过观察者(鼠标)的坐标计算得出,点在画布上的移动速度用点到观察者的假想距离计算出来,近的点移动快,远的点移动慢,这样有3D层次感;5.每个点的实际位置根据上一帧的实际位置和目标位置还有移动速度计算得出,这个计算结果用于第3步的重画步骤。

杨冬芳 2019-12-02 02:36:20 0 浏览量 回答数 0

回答

您只能将滚动条与几个小部件关联,而根小部件和框架不属于该小部件组。 最常见的解决方案是创建一个canvas小部件,并将滚动条与该小部件关联起来。然后,在画布中嵌入包含标签小部件的框架。确定框架的宽度/高度,并将其输入到画布滚动区域选项中,以便滚动区域与框架的大小完全匹配。 为什么要将小部件放在框架中而不是直接放在画布中呢?附加到画布上的滚动条只能滚动使用create_方法之一创建的项。不能使用包、位置或网格滚动添加到画布中的项。通过使用框架,您可以在框架内使用这些方法,然后为框架调用create_window一次。 将文本项直接绘制到画布上并不十分困难,因此,如果框架嵌入到画布中解决方案过于复杂,您可能需要重新考虑这种方法。因为您正在创建一个网格,所以每个文本项的坐标将非常容易计算,特别是如果每一行的高度相同(如果使用单一字体,则很可能是相同的高度)。 对于直接在画布上绘图,只需计算出使用的字体的行高(有相应的命令)。然后,每个y坐标为行*(lineheight+间距)。x坐标是一个固定的数字,基于每一列中最宽的项。如果为它所在的列赋予所有内容一个标记,则可以使用单个命令调整列中所有项的x坐标和宽度。 下面是一个使用面向对象方法的框架嵌入到画布解决方案的例子: import tkinter as tk # python 3 # import Tkinter as tk # python 2 class Example(tk.Frame): def __init__(self, root): tk.Frame.__init__(self, root) self.canvas = tk.Canvas(root, borderwidth=0, background="#ffffff") self.frame = tk.Frame(self.canvas, background="#ffffff") self.vsb = tk.Scrollbar(root, orient="vertical", command=self.canvas.yview) self.canvas.configure(yscrollcommand=self.vsb.set) self.vsb.pack(side="right", fill="y") self.canvas.pack(side="left", fill="both", expand=True) self.canvas.create_window((4,4), window=self.frame, anchor="nw", tags="self.frame") self.frame.bind("<Configure>", self.onFrameConfigure) self.populate() def populate(self): '''Put in some fake data''' for row in range(100): tk.Label(self.frame, text="%s" % row, width=3, borderwidth="1", relief="solid").grid(row=row, column=0) t="this is the second column for row %s" %row tk.Label(self.frame, text=t).grid(row=row, column=1) def onFrameConfigure(self, event): '''Reset the scroll region to encompass the inner frame''' self.canvas.configure(scrollregion=self.canvas.bbox("all")) if __name__ == "__main__": root=tk.Tk() Example(root).pack(side="top", fill="both", expand=True) root.mainloop() 这里有一个不使用类的解决方案: import tkinter as tk # python 3 # import Tkinter as tk # python 2 def populate(frame): '''Put in some fake data''' for row in range(100): tk.Label(frame, text="%s" % row, width=3, borderwidth="1", relief="solid").grid(row=row, column=0) t="this is the second column for row %s" %row tk.Label(frame, text=t).grid(row=row, column=1) def onFrameConfigure(canvas): '''Reset the scroll region to encompass the inner frame''' canvas.configure(scrollregion=canvas.bbox("all")) root = tk.Tk() canvas = tk.Canvas(root, borderwidth=0, background="#ffffff") frame = tk.Frame(canvas, background="#ffffff") vsb = tk.Scrollbar(root, orient="vertical", command=canvas.yview) canvas.configure(yscrollcommand=vsb.set) vsb.pack(side="right", fill="y") canvas.pack(side="left", fill="both", expand=True) canvas.create_window((4,4), window=frame, anchor="nw") frame.bind("<Configure>", lambda event, canvas=canvas: onFrameConfigure(canvas)) populate(frame) root.mainloop()

kun坤 2019-12-25 21:59:57 0 浏览量 回答数 0

问题

如何在每个Canvas DrawImage的forEach循环内添加自定义位置?

养狐狸的猫 2019-12-01 19:59:06 16 浏览量 回答数 0

回答

使用java的图形组件,swing,先创建画布canvas,然后在画布上调用swing的api画长方形,可以直接查api的每个参数意义

1038123254913072 2019-12-02 01:01:58 0 浏览量 回答数 0

问题

我的Kivy程序在左下角出现了一个随机的白色方块

一码平川MACHEL 2019-12-01 19:31:17 509 浏览量 回答数 1

问题

UITableView Cell 的复用的问题

杨冬芳 2019-12-01 20:11:46 1045 浏览量 回答数 1

回答

问题是你在不同的地方画了几次,恰好在函数changeBG中,你只需要在一个地方画画并将背景颜色设置为属性,这样当这个值改变时,Label将被重新绘制。 另一个错误是您正在创建一个不在.kv中使用的Container。 在while循环的情况下,可以使用for循环简化。 *的.py import random import kivykivy.require("1.10.1")from kivy.app import Appfrom kivy.lang import Builderfrom kivy.uix.floatlayout import FloatLayoutfrom kivy.uix.gridlayout import GridLayoutfrom kivy.uix.label import Labelfrom kivy.config import Config Config.set('graphics', 'width', '400')Config.set('graphics', 'height', '400') class Container(FloatLayout): pass class ColorLabel(Label): pass class Main(App): def build(self): Builder.load_file("EveryImage.kv") the_grid = GridLayout(cols=10, spacing=1) for _ in range(100): newLabel = ColorLabel() the_grid.add_widget(newLabel) if random.choice([True, False]): newLabel.bg_color = [0,0,0,1] root = Container() root.add_widget(the_grid) return root Keep everything below this last! if name == '__main__': Main().run() .kv Container holds all the other layouts : id: contain canvas.before: Color: rgba: 0,0,0.5,1 #blue, just for the grid Rectangle: pos: self.pos size: self.size : bg_color: 1, 1, 1, 1 canvas.before: Color: rgba: self.bg_color # white Rectangle: pos: self.pos size: self.size

一码平川MACHEL 2019-12-02 01:53:24 0 浏览量 回答数 0

问题

Vue.extend创建的组件propsData上接收的值改变了但是视图没有更新! vue.jsja

问问小秘 2020-01-09 15:58:07 3 浏览量 回答数 1

问题

Android 的Paint(画笔)及Canvas(画布)? 400 报错

爱吃鱼的程序员 2020-05-29 20:04:37 0 浏览量 回答数 1

问题

Android 的Paint(画笔)及Canvas(画布)? 400 报错

优选2 2020-06-05 16:56:03 0 浏览量 回答数 1

问题

【技术干货】前端工程师看过来!一种Ajax缩放图片上传的办法 !

驻云科技 2019-12-01 21:08:17 6525 浏览量 回答数 2

回答

从效果图上我们还看到有几点重要信息: 点赞动画图片大小不一,运动轨迹也是随机的点赞动画图片都是先放大再匀速运动。快到顶部的时候,是渐渐消失。收到大量的点赞请求的时候,点赞动画不扎堆,井然有序持续出现。 那么如何实现这些要求呢?下面介绍两种实现方式来实现(底部附完整 demo): CSS3 实现 用 CSS3 实现动画,显然,我们想到的是用 animation 。 首先看下 animation 合并写法,具体含义就不解释了,如果需要可以自行了解。 animation: name duration timing-function delay iteration-count direction fill-mode play-state; 我们开始来一步一步实现。 Step 1: 固定区域,设置基本样式 首先,我们先准备 1 张点赞动画图片: 看一下 HTML 结构。外层一个结构固定整个显示动画区域的位置。这里在一个宽 100px ,高 200px 的 div 区域。 Step 5: 设置偏移 我们先定义帧动画:bubble_1 来执行偏移。图片开始放大阶段,这里没有设置偏移,保持中间原点不变。 在运行到 25% * 4 = 1s,即 1s之后,是向左偏移 -8px, 2s 的时候,向右偏移 8px,3s 的时候,向做偏移 15px ,最终向右偏移 15px。 大家可以想到了,这是定义的一个经典的左右摆动轨迹,“向左向右向左向右” 曲线摆动效果。 @keyframes bubble_1 { 0% { } 25% { margin-left:-8px; } 50% { margin-left:8px } 75% { margin-left:-15px } 100% { margin-left:15px } } 效果如下: Step 6: 补齐动画样式 这里预设了一种运行曲线轨迹,左右摆动的样式,我们在再预设更多种曲线,达到随机轨迹的目的。 比如 bubble_1 的左右偏移动画轨迹,我们可以修改偏移值,来达到不同的曲线轨迹。 Step 7: JS 操作随机增加节点样式 提供增加点赞的方法,随机将点赞的样式组合,然后渲染到节点上。 let praiseBubble = document.getElementById("praise_bubble"); let last = 0; function addPraise() { const b =Math.floor(Math.random() * 6) + 1; const bl =Math.floor(Math.random() * 11) + 1; // bl1~bl11 let d = document.createElement("div"); d.className = bubble b${b} bl${bl}; d.dataset.t = String(Date.now()); praiseBubble.appendChild(d); } setInterval(() => { addPraise(); },300) 在使用 CSS 来实现点赞的时候,通常还需要注意设置 bubble 的随机延时,比如: .bl2{ animation:bubble_2 $bubble_time linear .4s 1 forwards,bubble_big_2 $bubble_scale linear .4s 1 forwards,bubble_y $bubble_time linear .4s 1 forwards; } 这里如果是随机到 bl2,那么延时 0.4s 再运行,bl3 延时 0.6s …… 如果是批量更新到节点上,不设置延时的话,那就会扎堆出现。随机“ bl ”样式,就随机了延时,然后批量出现,都会自动错峰显示。当然,我们还需要增加当前用户手动点赞的动画,这个不需要延时。 另外,有可能同时别人下发了点赞 40 个,业务需求通常是希望这 40 个点赞气泡都能依次出现,制造持续的点赞氛围,否则下发量大又会扎堆显示了。 那么我们还需要分批打散点赞数量,比如一次点赞的时间($bubble_time)是 4s, 那么 4s 内,希望同时出现多少个点赞呢?比如是 10个,那么 40 个点赞,需要分批 4 次渲染。 window.requestAnimationFrame(() => { // 继续循环处理批次 render(); }); 另外还需要手动清除节点。以防节点过多带来的性能问题。如下是完整的效果图。 class ThumbsUpAni{ constructor(){ const canvas = document.getElementById('thumsCanvas'); this.context = canvas.getContext('2d')!; this.width = canvas.width; this.height = canvas.height; } } Step 2:创建渲染对象 实时渲染图片,使其变成一个连贯的动画,很重要的是:生成曲线轨迹。这个曲线轨迹需要是平滑的均匀曲线。 假如生成的曲线轨迹不平滑的话,那看到的效果就会太突兀,比如上一个是 10 px,下一个就是 -10px,那显然,动画就是忽左忽右左右闪烁了。 理想的轨迹是上一个位置是 10px,接下来是 9px,然后一直平滑到 -10px,这样的坐标点就是连贯的,看起来动画就是平滑运行。 随机平滑 X 轴偏移 如果要做到平滑曲线,其实可以使用我们再熟悉不过的正弦( Math.sin )函数来实现均匀曲线。 看下图的正弦曲线: 这是 Math.sin(0) 到 Math.sin(9) 的曲线图走势图,它是一个平滑的从正数到负数,然后再从负数到正数的曲线图,完全符合我们的需求,于是我们再需要生成一个随机比率值,让摆动幅度随机起来。 const angle = getRandom(2, 10); let ratio = getRandom(10,30)*((getRandom(0, 1) ? 1 : -1)); const getTranslateX = (diffTime) => { if (diffTime < this.scaleTime) {// 放大期间,不进行摇摆偏移 return basicX; } else { return basicX + ratio*Math.sin(angle*(diffTime - this.scaleTime)); } }; 复制代码scaleTime 是从开始放大到最终大小,用多长时间,这里我们设置 0.1,即总共运行时间前面的 10% 的时间,点赞图片逐步放大。 diffTime,是只从开始动画运行到当前时间过了多长时间了,为百分比。实际值是从 0 --》 1 逐步增大。 diffTime - scaleTime = 0 ~ 0.9, diffTime 为 0.4 的时候,说明是已经运行了 40% 的时间。 因为 Math.sin(0) 到 Math.sin(0.9) 曲线几乎是一个直线,所以不太符合摆动效果,从 Math.sin(0) 到 Math.sin(1.8) 开始有细微的变化,所以我们这里设置的 angle 最小值为 2。 这里设置角度系数 angle 最大为 10 ,从底部到顶部运行两个波峰。 当然如果运行距离再长一些,我们可以增大 angle 值,比如变成 3 个波峰(如果时间短,出现三个波峰,就会运行过快,有闪烁现象)。如下图: Y 轴偏移 这个容易理解,开始 diffTime 为 0 ,所以运行偏移从 this.height --> image.height / 2。即从最底部,运行到顶部留下,实际上我们在顶部会淡化隐藏。 const getTranslateY = (diffTime) => { return image.height / 2 + (this.height - image.height / 2) * (1-diffTime); }; 复制代码放大缩小 当运行时间 diffTime 小于设置的 scaleTime 的时候,按比例随着时间增大,scale 变大。超过设置的时间阈值,则返回最终大小。 const basicScale = [0.6, 0.9, 1.2][getRandom(0, 2)]; const getScale = (diffTime) => { if (diffTime < this.scaleTime) { return +((diffTime/ this.scaleTime).toFixed(2)) * basicScale; } else { return basicScale; } }; 复制代码淡出 同放大逻辑一致,只不过淡出是在运行快到最后的位置开始生效。 **const fadeOutStage = getRandom(14, 18) / 100; const getAlpha = (diffTime) => { let left = 1 - +diffTime; if (left > fadeOutStage) { return 1; } else { return 1 - +((fadeOutStage - left) / fadeOutStage).toFixed(2); } }; ** 实时绘制 创建完绘制对象之后,就可以实时绘制了,根据上述获取到的“偏移值”,“放大”和“淡出”值,然后实时绘制点赞图片的位置即可。 每个执行周期,都需要重新绘制 canvas 上的所有的动画图片位置,最终形成所有的点赞图片都在运动的效果。 createRender(){ return (diffTime) => { // 差值满了,即结束了 0 ---》 1 if(diffTime>=1) return true; context.save(); const scale = getScale(diffTime); const translateX = getTranslateX(diffTime); const translateY = getTranslateY(diffTime); context.translate(translateX, translateY); context.scale(scale, scale); context.globalAlpha = getAlpha(diffTime); // const rotate = getRotate(); // context.rotate(rotate * Math.PI / 180); context.drawImage( image, -image.width / 2, -image.height / 2, image.width, image.height ); context.restore(); }; } 这里绘制的图片是原图的 width 和 height。前面我们设置了 basiceScale,如果图片更大,我们可以把 scale 再变小即可。 const basicScale = [0.6, 0.9, 1.2][getRandom(0, 2)]; 实时绘制扫描器 开启实时绘制扫描器,将创建的渲染对象放入 renderList 数组,数组不为空,说明 canvas 上还有动画,就需要不停的去执行 scan,直到 canvas 上没有动画结束为止。 scan() { this.context.clearRect(0, 0, this.width, this.height); this.context.fillStyle = "#f4f4f4"; this.context.fillRect(0,0,200,400); let index = 0; let length = this.renderList.length; if (length > 0) { requestAnimationFrame(this.scan.bind(this)); } while (index < length) { const render = this.renderList[index]; if (!render || !render.render || render.render.call(null, (Date.now() - render.timestamp) / render.duration)) { // 结束了,删除该动画 this.renderList.splice(index, 1); length--; } else { // 当前动画未执行完成,continue index++; } } } 这里就是根据执行的时间来对比,判断动画执行到的位置了: diffTime = (Date.now() - render.timestamp) / render.duration 复制代码如果开始的时间戳是 10000,当前是100100,则说明已经运行了 100 毫秒了,如果动画本来需要执行 1000 毫秒,那么 diffTime = 0.1,代表动画已经运行了 10%。 增加动画 每点赞一次或者每接收到别人点赞一次,则调用一次 start 方法来生成渲染实例,放进渲染实例数组。如果当前扫描器未开启,则需要启动扫描器,这里使用了 scanning 变量,防止开启多个扫描器。 start() { const render = this.createRender(); const duration = getRandom(1500, 3000); this.renderList.push({ render, duration, timestamp: Date.now(), }); if (!this.scanning) { this.scanning = true; requestFrame(this.scan.bind(this)); } return this; } 这里开启定时器,记录定时器里面处理的 thumbsStart 的值,如果有新增点赞,且定时器还在运行,直接更新最后的 praiseLast 值,定时器会依次将点赞请求全部处理完。 定时器的延时时间 time 根据开启定时器的时候,需要渲染多少点赞动画来决定的,比如需要渲染 100 个点赞动画,我们将 100 个点赞动画分布在 5s 内渲染完。 对于热门直播,会同时渲染的动画很多,不会扎堆显示,且动画完全能衔接上,不停的冒泡点赞动画。对于冷门直播,有多余一个的点赞请求,我们能打散到 5s 内显示,也不会扎堆显示。 End 两种方式渲染点赞动画都已经完成,完整源码,源码戳这里 。 源码运行效果图: 再比较 这两种实现方式,都可以满足要求,那么到底哪种更优呢? 我们来看下两者的数据对比。以下为未开启硬件加速的对比,采用不间断疯狂渲染点赞动画的数据对比: 整体来说,差异如下: CSS3 实现简单Canvas 更灵活,操作更细腻CSS3 内存消耗比 Canvas 大,如果开启硬件加速,内存消耗更大一些。

剑曼红尘 2020-04-15 19:17:18 0 浏览量 回答数 0

回答

自定义控件: 1、组合控件。这种自定义控件不需要我们自己绘制,而是使用原生控件组合成的新控件。如标题栏。 2、继承原有的控件。这种自定义控件在原生控件提供的方法外,可以自己添加一些方法。如制作圆角,圆形图片。 3、完全自定义控件:这个View上所展现的内容全部都是我们自己绘制出来的。比如说制作水波纹进度条。 View的绘制流程:OnMeasure()——>OnLayout()——>OnDraw() 第一步:OnMeasure():测量视图大小。从顶层父View到子View递归调用measure方法,measure方法又回调OnMeasure。 第二步:OnLayout():确定View位置,进行页面布局。从顶层父View向子View的递归调用view.layout方法的过程,即父View根据上一步measure子View所得到的布局大小和布局参数,将子View放在合适的位置上。 第三步:OnDraw():绘制视图。ViewRoot创建一个Canvas对象,然后调用OnDraw()。六个步骤:①、绘制视图的背景;②、保存画布的图层(Layer);③、绘制View的内容;④、绘制View子视图,如果没有就不用; ⑤、还原图层(Layer);⑥、绘制滚动条。

剑曼红尘 2020-04-10 12:41:49 0 浏览量 回答数 0

回答

html: [html] view plain copy <span style="white-space:pre"> </span><audio controls autoplay></audio> <input type="button" value="开始录音" onclick="startRecording()"/> <input type="button" value="获取录音" onclick="obtainRecord()"/> <input type="button" value="停止录音" onclick="stopRecord()"/> <input type="button" value="播放录音" onclick="playRecord()"/> <video id="video1" width="320px" height="240px" controls autoplay ></video> <video id="video2" width="320px" height="240px" controls autoplay ></video> <canvas id="canvas1" width="320" height="240"></canvas> <input type="button" value="拍摄" onclick="scamera()"/> <input type="button" value="播放视频" onclick="playVideo()"/> js文件: [javascript] view plain copy (function (window) { //兼容 window.URL = window.URL || window.webkitURL; navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia; var HZRecorder = function (stream, config) { config = config || {}; config.sampleBits = config.sampleBits || 8; //采样数位 8, 16 config.sampleRate = config.sampleRate || (44100 / 6); //采样率(1/6 44100) //创建一个音频环境对象 audioContext = window.AudioContext || window.webkitAudioContext; var context = new audioContext(); //将声音输入这个对像 var audioInput = context.createMediaStreamSource(stream); //设置音量节点 var volume = context.createGain(); audioInput.connect(volume); //创建缓存,用来缓存声音 var bufferSize = 4096; // 创建声音的缓存节点,createScriptProcessor方法的 // 第二个和第三个参数指的是输入和输出都是双声道。 var recorder = context.createScriptProcessor(bufferSize, 2, 2); var audioData = { size: 0 //录音文件长度 , buffer: [] //录音缓存 , inputSampleRate: context.sampleRate //输入采样率 , inputSampleBits: 16 //输入采样数位 8, 16 , outputSampleRate: config.sampleRate //输出采样率 , oututSampleBits: config.sampleBits //输出采样数位 8, 16 , input: function (data) { this.buffer.push(new Float32Array(data)); this.size += data.length; } , compress: function () { //合并压缩 //合并 var data = new Float32Array(this.size); var offset = 0; for (var i = 0; i < this.buffer.length; i++) { data.set(this.buffer[i], offset); offset += this.buffer[i].length; } //压缩 var compression = parseInt(this.inputSampleRate / this.outputSampleRate); var length = data.length / compression; var result = new Float32Array(length); var index = 0, j = 0; while (index < length) { result[index] = data[j]; j += compression; index++; } return result; } , encodeWAV: function () { var sampleRate = Math.min(this.inputSampleRate, this.outputSampleRate); var sampleBits = Math.min(this.inputSampleBits, this.oututSampleBits); var bytes = this.compress(); var dataLength = bytes.length * (sampleBits / 8); var buffer = new ArrayBuffer(44 + dataLength); var data = new DataView(buffer); var channelCount = 1;//单声道 var offset = 0; var writeString = function (str) { for (var i = 0; i < str.length; i++) { data.setUint8(offset + i, str.charCodeAt(i)); } }; // 资源交换文件标识符 writeString('RIFF'); offset += 4; // 下个地址开始到文件尾总字节数,即文件大小-8 data.setUint32(offset, 36 + dataLength, true); offset += 4; // WAV文件标志 writeString('WAVE'); offset += 4; // 波形格式标志 writeString('fmt '); offset += 4; // 过滤字节,一般为 0x10 = 16 data.setUint32(offset, 16, true); offset += 4; // 格式类别 (PCM形式采样数据) data.setUint16(offset, 1, true); offset += 2; // 通道数 data.setUint16(offset, channelCount, true); offset += 2; // 采样率,每秒样本数,表示每个通道的播放速度 data.setUint32(offset, sampleRate, true); offset += 4; // 波形数据传输率 (每秒平均字节数) 单声道×每秒数据位数×每样本数据位/8 data.setUint32(offset, channelCount * sampleRate * (sampleBits / 8), true); offset += 4; // 快数据调整数 采样一次占用字节数 单声道×每样本的数据位数/8 data.setUint16(offset, channelCount * (sampleBits / 8), true); offset += 2; // 每样本数据位数 data.setUint16(offset, sampleBits, true); offset += 2; // 数据标识符 writeString('data'); offset += 4; // 采样数据总数,即数据总大小-44 data.setUint32(offset, dataLength, true); offset += 4; // 写入采样数据 if (sampleBits === 8) { for (var i = 0; i < bytes.length; i++, offset++) { var s = Math.max(-1, Math.min(1, bytes[i])); var val = s < 0 ? s * 0x8000 : s * 0x7FFF; val = parseInt(255 / (65535 / (val + 32768))); data.setInt8(offset, val, true); } } else { for (var i = 0; i < bytes.length; i++, offset += 2) { var s = Math.max(-1, Math.min(1, bytes[i])); data.setInt16(offset, s < 0 ? s * 0x8000 : s * 0x7FFF, true); } } return new Blob([data], { type: 'audio/wav' }); } }; //开始录音 this.start = function () { audioInput.connect(recorder); recorder.connect(context.destination); }; //停止 this.stop = function () { recorder.disconnect(); }; //获取音频文件 this.getBlob = function () { this.stop(); return audioData.encodeWAV(); }; //回放 this.play = function (audio) { audio.src = window.URL.createObjectURL(this.getBlob()); }; //上传 this.upload = function (url, callback) { var fd = new FormData(); fd.append('audioData', this.getBlob()); var xhr = new XMLHttpRequest(); if (callback) { xhr.upload.addEventListener('progress', function (e) { callback('uploading', e); }, false); xhr.addEventListener('load', function (e) { callback('ok', e); }, false); xhr.addEventListener('error', function (e) { callback('error', e); }, false); xhr.addEventListener('abort', function (e) { callback('cancel', e); }, false); } xhr.open('POST', url); xhr.send(fd); }; //音频采集 recorder.onaudioprocess = function (e) { audioData.input(e.inputBuffer.getChannelData(0)); //record(e.inputBuffer.getChannelData(0)); }; }; //抛出异常 HZRecorder.throwError = function (message) { throw new function () { this.toString = function () { return message; };}; }; //是否支持录音 HZRecorder.canRecording = (navigator.getUserMedia != null); //获取录音机 HZRecorder.get = function (callback, config) { if (callback) { if (navigator.getUserMedia) { navigator.getUserMedia( { audio: true } //只启用音频 , function (stream) { var rec = new HZRecorder(stream, config); callback(rec); } , function (error) { switch (error.code || error.name) { case 'PERMISSION_DENIED': case 'PermissionDeniedError': HZRecorder.throwError('用户拒绝提供信息。'); break; case 'NOT_SUPPORTED_ERROR': case 'NotSupportedError': HZRecorder.throwError('<a href="http://www.it165.net/edu/ewl/" target="_blank" class="keylink">浏览器</a>不支持硬件设备。'); break; case 'MANDATORY_UNSATISFIED_ERROR': case 'MandatoryUnsatisfiedError': HZRecorder.throwError('无法发现指定的硬件设备。'); break; default: HZRecorder.throwError('无法打开麦克风。异常信息:' + (error.code || error.name)); break; } }); } else { HZRecorder.throwErr('当前<a href="http://www.it165.net/edu/ewl/" target="_blank" class="keylink">浏览器</a>不支持录音功能。'); return; } } }; window.HZRecorder = HZRecorder; })(window); 页面调用 js: [javascript] view plain copy /* 音频 */ var recorder; var audio = document.querySelector('audio'); function startRecording() { HZRecorder.get(function (rec) { recorder = rec; recorder.start(); }); } function obtainRecord(){ var record = recorder.getBlob(); debugger; }; function stopRecord(){ recorder.stop(); }; function playRecord(){ recorder.play(audio); }; /* 视频 */ function scamera() { var videoElement = document.getElementById('video1'); var canvasObj = document.getElementById('canvas1'); var context1 = canvasObj.getContext('2d'); context1.fillStyle = "#ffffff"; context1.fillRect(0, 0, 320, 240); context1.drawImage(videoElement, 0, 0, 320, 240); }; function playVideo(){ var videoElement1 = document.getElementById('video1'); var videoElement2 = document.getElementById('video2'); videoElement2.setAttribute("src", videoElement1);

小旋风柴进 2019-12-02 02:37:49 0 浏览量 回答数 0

回答

Composite ViewComposite views (也被称为 compound views) 是众多将多个view结合成为一个可重用UI组件的方法中最简单的。这种方法的实现过程是这样的:继承相关的内建的布局。在构造函数里面填充一个 merge 布局。初始化成员变量并通过 findViewById()指向内部view。添加自定义的API来查询和更新view的状态。TweetCompositeViewcode 就是一个 composite view。它继承于 RelativeLayout,并填充了 tweet_composite_layout.xmlcode 布局文件,最后向外界暴露了 update()方法来更新它在adaptercode里面的状态。Custom Composite View上面提到的TweetCompositeView 这种实现方式能满足大部分的情况。但是碰到某些情况就不灵了。假设你现在想要减少子视图的数量,让布局元素的便利更加有效。这个时候我们可以回过头来看看,尽管 composite views 实现起来比较简单,但是使用这些内建的布局还是有不少的开销的——特别是 LinearLayout 和RelativeLayout这种比较复杂的容器。由于Android平台内建布局的实现,在一次布局元素遍历中,系统需要处理许多布局的结合和子视图的多次测量——LinearLayout的 layout_weight 的属性就是常见例子。因此你可以为你的app量身定做一套子视图的计算和定位逻辑,这样的话你就可以极大的优化你的UI了。这种做法就是我接下来要介绍的 custom composite view.顾名思义,一个 custom composite view 就是一个重写了onMeasure() 和onLayout() 方法的 composite view 。因此相比之前的composite view继承了 RelativeLayout,现在我们需要更进一步——继承更抽象的ViewGroup。TweetLayoutViewcode 就是通过这种技术实现的。注意现在这个实现不像 TweetComposiveView 继承了LinearLayout ,这也就避免了 layout_weightcode这个属性的使用了。这个大费周折的过程通过ViewGroup’s 的measureChildWithMargins() 方法和背后的 getChildMeasureSpec() 方法计算出了每个子视图的 MeasureSpec 。TweetLayoutView 不能正确地处理所有可能的 layout 组合但是它也不必这样。我们肯定需要根据特定需求来优化我们的自定义布局,这种方式可以让我们写出简单高效的布局代码。Flat Custom View如你所见,custom composite views 可以简单地通过使用ViewGroup 的API就可以实现了。大部分时候,这种实现是可以满足我们的需求的。然而我们想更进一步的话——优化我们应用中的关键部分UI,比如 ListViews ,ViewPager等等。如果我们把所有的 TweetLayoutView 子视图合并成一个单一的自定义视图然后统一管理会怎么样呢?这就是我们接下来要讨论的 flat custom view——参看下面的图片。layouts左边为CUSTOM COMPOSITE VIEW ,右边是FLAT CUSTOM VIEWflat custom view 就是一个完全自定义的 view ,它完全负责内部的子视图的计算,位置安排,绘制。所以它就直接继承了View 而不是 ViewGroup。如果你想找找现实生活中app是否存在这样的例子,很简单——开启你手机“开发者模式”里面的 “显示布局边界”选项,然后打开 Twitter, Gmail, 或者 Pocket这些app,它们在列表UI里面都采用了 flat custom view。使用 flat custom view最主要的好处就是可以极大地压缩app 的视图层级,进而可以进行更快的布局元素遍历,最终可以减少内存占用。Flat custom view 可以给你最大的自由,就好像你在一张白纸上面作画。但是这样的自由是有代价的:你不能使用已有的那些视图元素了,比如 TextView 和 ImageView。没错,在 Canvas 上面描绘文本 的确很简单,但要你实现 ellipsizing(就是对过长的文本截断)呢?同样, 在 Canvas 上面 描绘图片确很简单,但是如何缩放呢?这些限制同样适用于touch events, accessibility, keyboard navigation等等。所以使用flat custom view的底线就是:只将flat custom view应用于你的app的UI核心部分,其他的就直接依赖Android平台提供的view了。TweetElementViewcode 就是 flat custom view。为了更容易的实现它,我创建了一个小小的自定义视图框架叫做UIElement。你可以在 canvascode 这个包里找到它。UIElement 提供了和Android平台类似的 measure/layout API 。它包含了没有图像界面的 TextView 和 ImageView ,这两个元素包含了几个必需的特性——分别参看 TextElementcode 和ImageElementcode 。它还拥有自己的 inflatercode ,帮助从 布局资源文件code里面实例化UIElement 。注意: UIElement 还处于非常早期的开发阶段,所以还有很多缺陷,不过将来随着不断的改进UIElement 可能会变得非常有用。你可能觉得TweetElementView 的代码看起来很简单,这是因为实际代码都在 TweetElementcode里面——实际上TweetElementView 扮演托管的角色code。TweetElement 里面的布局代码和TweetLayoutView‘非常类似,但是它使用 Picasso 请求图片时却不一样code ,因为TweetElement 没有使用ImageView。Async Custom View总所周知,Android UI 框架时单线程的 。 这样的单线程会带来一些限制。比如,你不能在主线程之外遍历布局元素——然而这对复杂、动态的UI是很有益处的。假如你的app 在一个ListView 中很布局比较复杂的条目(就像大多数社交app一样),那么你在滑动ListView 就很有可能出现跳帧的现象,因为ListView 需要为列表中即将出现的新内容计算它们的视图大小code和布局code。同样的问题也会出现在GridViews,ViewPagers等等。如果我们可以在主线程之外的线程上面对那些还没有出现的子视图进行布局遍历是不是就可以解决上面的问题了?也就是说,在子视图上面调用 measure() 和layout() 方法都不会占用主线程的时间了。所以 async custom view 就是一个允许子视图布局遍历过程发生在主线程之外的实验,这个idea是受到Facebook的Paperteam async node framework 这个视频激发所想到的。既然我们在主线程之外永远接触不到Android平台的UI组件,因此我们需要一个API在不能直接接触到这个视图的前提下对这个视图的内容进行测量、布局。这恰恰就是 UIElement 框架提供给我的功能。AsyncTweetViewcode 就是一个 async custom view。它使用了一个线程安全的 AsyncTweetElementcode 工厂类code 来定义它的内容。具体过程是一个 Smoothie 子项加载器code 在一个后台线程上对暂时不可见的AsyncTweetElement 进行创建、预测量和缓存(在内存里面,以便后来直接使用)。当然在实现这个异步UI的过程中我还是妥协了一些,因为你不知道如何显示任意高度的布局占位符。比如,当布局异步传递过来的时候你只能在后台线程对它们的大小进行一次更改。因此当一个 AsyncTweetView 就要显示的时候却无法在内存里面找到合适的AsyncTweetElement ,这个时候框架就会强制在主线程上面创建一个AsyncTweetElement code。还有,预先加载的逻辑和内存缓存过期时间设置都需要比较好的实现来保证在主线程尽可能多地利用内存里面的缓存布局。比如,这个方案中使用 LRU 缓存code 就不是一个明智的选择。尽管还存在这些限制,但是使用 async custom view 的得到的初步结果还是很有前途的。当然我也会通过重构这个UIElement 框架和使用其他类别的UI在这个领域继续探索。让我们静观其变吧。总结在我们涉及到布局的时候,我们自定义的越深,我们能从Android平台所能获得的依赖就越少。所以我们也要避免过早优化,只在确实能实实在在改善app质量和性能的区域进行完全的布局自定义。这不是一个非黑即白的决定。在使用平台提供的UI元素和完全自定义的两种极端之间还有很多方案——从简单的composite views 到复杂的 async views。实际项目中,你可能会结合文中的几种方案写出优秀的app。

51干警网 2019-12-02 01:34:26 0 浏览量 回答数 0

回答

方式一:Coding JavaScript <!--[if lt IE 9]> <script> (function() { if (! /*@cc_on!@*/ 0) return; var e = "abbr, article, aside, audio, canvas, datalist, details, dialog, eventsource, figure, footer, header, hgroup, mark, menu, meter, nav, output, progress, section, time, video".split(', '); var i= e.length; while (i--){ document.createElement(e[i]) } })() </script> <![endif]--> 如果是IE9以下的IE浏览器将创建HTML5标签, 这样非IE浏览器就会忽视这段代码,也就不会有无谓的http请求了。 第二种方法:使用Google的html5shiv包(推荐) <!--[if lt IE 9]> <script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script> <![endif]--> 但是不管使用以上哪种方法,都要初始化新标签的CSS.因为HTML5在默认情况下表现为内联元素,对这些元素进行布局我们需要利用CSS手工把它们转为块状元素方便布局 /*html5*/ article,aside,dialog,footer,header,section,footer,nav,figure,menu{display:block} 但是如果ie6/7/8 禁用脚本的用户,那么就变成了无样式的"白板"网页,我们该怎么解决呢? 我们可以参照facebook的做法,即引导用户进入带有noscript标识的 “/?_fb_noscript=1”页面,用 html4 标签替换 html5 标签,这要比为了保持兼容性而写大量 hack 的做法更轻便一些。 <!--[if lte IE 8]> <noscript> <style>.html5-wrappers{display:none!important;}</style> <div class="ie-noscript-warning">您的浏览器禁用了脚本,请<a href="">查看这里</a>来启用脚本!或者<a href="/?noscript=1">继续访问</a>. </div> </noscript> <![endif]--> 这样可以引导用户开启脚本,或者直接跳转到HTML4标签设计的界面。

a123456678 2019-12-02 02:21:18 0 浏览量 回答数 0

问题

移植到 Direct3D 11:报错 

kun坤 2020-06-04 21:07:16 6 浏览量 回答数 1

问题

使用Yeoman和Polymer创建Web应用:报错

kun坤 2020-06-08 11:03:09 3 浏览量 回答数 1

问题

寻找更干净的方式来编写这个Tkinter代码使用循环

kun坤 2019-12-26 10:46:34 1 浏览量 回答数 1

问题

activiti 流程设计器传入modelID显示空白,提示namespace找不到?报错

爱吃鱼的程序员 2020-06-06 15:26:54 0 浏览量 回答数 1

问题

一个activiti集成的问题,求助各位大神?报错

爱吃鱼的程序员 2020-06-06 10:55:10 0 浏览量 回答数 1

回答

前言 前两天在 echarts 上寻找灵感的时候,看到了很多有关地图类似的例子,地图定位等等,但是好像就是没有地铁线路图,就自己花了一些时间捣鼓出来了这个交互式地铁线路图的 Demo,地铁线路上的点是在网上随便下载了一个,这篇文章记录自己的一些收获(毕竟我还是个菜鸟)以及代码的实现,希望能够帮到一些朋友。当然,如果有什么意见的可以直接跟我说,大家一起交流才会进步。 效果图 地图稍微内容有点多,要全部展示,字显得有点小了,但是没关系,可以按照需求放大缩小,字体和绘制的内容并不会失真,毕竟都是用矢量绘制的~ 界面生成 底层的 div 是通过 ht.graph.GraphView 组件生成的,然后就可以利用 HT for Web 提供好的方法,调用 canvas 画笔随便绘制就好,先来看看怎么生成底层 div: var dm = new ht.DataModel();//数据容器 var gv = new ht.graph.GraphView(dm);//拓扑组件 gv.addToDOM();//将拓扑图组件添加进body中 addToDOM 函数声明如下: addToDOM = function(){ var self = this, view = self.getView(), style = view.style; document.body.appendChild(view); //将组件底层div添加到body中 style.left = '0';//默认组件是绝对定位,所以要设置位置 style.right = '0'; style.top = '0'; style.bottom = '0'; window.addEventListener('resize', function () { self.iv(); }, false); //窗口变化事件 } 现在我就可以在这个 div 上乱涂乱画了~首先我获取下载好的地铁线路图上的点,我将它们放在 subway.js 中,这个 js 文件全部都是下载的内容,我没有做其他的改动,主要是将这些点根据线路来分分配添加到数组中,比如: mark_Point13 = [];//线路 数组内包含线路的起点和终点坐标以及这条线路的名称 t_Point13 = [];//换成站点 数组内包含线路中的换乘站点坐标以及换成站点名称 n_Point13 = [];//小站点 数组内包含线路中的小站点坐标以及小站点名称 mark_Point13.push({ name: '十三号线', value: [113.4973,23.1095]}); mark_Point13.push({ name: '十三号线', value: [113.4155,23.1080]}); t_Point13.push({ name: '鱼珠', value: [113.41548,23.10547]}); n_Point13.push({ name: '裕丰围', value: [113.41548,23.10004]}); 接下来来描绘地铁线路,我声明了一个数组 lineNum,用来装 js 中所有的地铁线路的编号,以及一个 color 数组,用来装所有的地铁线的颜色,这些颜色的 index 与 lineNum 中地铁线编号的 index 是一一对应的: var lineNum = ['1', '2', '3', '30', '4', '5', '6', '7', '8', '9', '13', '14', '32', '18', '21', '22', '60', '68']; var color = ['#f1cd44', '#0060a1', '#ed9b4f', '#ed9b4f', '#007e3a', '#cb0447', '#7a1a57', '#18472c', '#008193', '#83c39e', '#8a8c29', '#82352b', '#82352b', '#09a1e0', '#8a8c29', '#82352b', '#b6d300', '#09a1e0']; 接着遍历 lineNum,将 lineNum 中的元素和颜色传到 createLine 函数中,根据这两个参数来绘制地铁线路以及配色,毕竟 js 文件中的命名方式也是有规律的,哪一条线路,则命名后面一定会加上对应的数字,所以我们只需要将字符串与这个编号结合即可获得 js 中对应的数组了: let lineName = 'Line' + num; let line = window[lineName]; createLine 的定义也非常简单,我的代码设置了不少的样式,所以看起来有点多。创建一个 ht.Polyline 管线,我们可以通过 polyline.addPoint() 函数向这个变量中添加具体的点,通过 setSegments 可以设置点的连接方式。 function createLine(num, color) {//绘制地图线 var polyline = new ht.Polyline();//多边形 管线 polyline.setTag(num);//设置节点tag标签,作为唯一标示 if(num === '68') polyline.setToolTip('A P M');//设置提示信息 else if(num === '60') polyline.setToolTip('G F'); else polyline.setToolTip('Line' + num); if(color) { polyline.s({//s 为 setStyle 的简写,设置样式 'shape.border.width': 0.4,//设置多边形的边框宽度 'shape.border.color': color,//设置多边形的边框颜色 'select.width': 0.2,//设置选中节点的边框宽度 'select.color': color//设置选中节点的边框颜色 }); } let lineName = 'Line' + num; let line = window[lineName]; for(let i = 0; i < line.length; i++) { for(let j = 0; j < line[i].coords.length; j++) { polyline.addPoint({x: line[i].coords[j][0]*300, y: -line[i].coords[j][1]*300}); if(num === '68'){//APM线(有两条,但是点是在同一个数组中的) if(i === 0 && j === 0) { polyline.setSegments([1]); } else if(i === 1 && j === 0) { polyline.getSegments().push(1); } else { polyline.getSegments().push(2); } } } } polyline.setLayer('0');//将线设置在下层,点设置在上层“top” dm.add(polyline);//将管线添加进数据容器中储存,不然这个管线属于“游离”状态,是不会显示在拓扑图上的 return polyline; } 上面代码中添加地铁线上的点有分为几种情况,是因为 js 中设置线的时候 Line68 有一个“跳跃”点的现象,所以我们必须“跳跃”过去,篇幅有限 Line68 数组具体的声明自行看 subway.js。 这里说明一点,如果用的是 addPoint 函数,不设置 segments 时,默认将添加进的点用直线连接,segments 的定义如下: 1: moveTo,占用 1 个点信息,代表一个新路径的起点 2: lineTo,占用 1 个点信息,代表从上次最后点连接到该点 3: quadraticCurveTo,占用 2 个点信息,第一个点作为曲线控制点,第二个点作为曲线结束点 4: bezierCurveTo,占用 3 个点信息,第一和第二个点作为曲线控制点,第三个点作为曲线结束点 5: closePath,不占用点信息,代表本次路径绘制结束,并闭合到路径的起始点 所以我们要做“跳跃”的行为设置 segments 为 1 即可。 最后绘制这些地铁线上的点,这个部分 subway.js 中也分离出来了,命名以“mark_Point”、“t_Point”以及“n_Point”开头,我在前面 js 的展示部分有对这些数组进行解释,大家动动中指划上去看看。 我们在这些点的位置添加 ht.Node 节点,当节点一添加进 dm 数据容器中时,就会在拓扑图上显示,当然,前提是这个拓扑图组件 gv 设置的数据容器是这个 dm。篇幅有限,添加地铁线上的点的代码部分我只展示添加“换乘站点”的点: var tName = 't_Point' + num; var tP = window[tName];//大站点 if(tP) {//有些线路没有“换乘站点” for(let i = 0; i < tP.length; i++) { let node = createNode(tP[i].name, tP[i].value, color[index]);//在获取的线路上的点的坐标位置添加节点 node.s({//设置节点的样式style 'label.scale': 0.05,//文本缩放,可以避免浏览器限制的最小字号问题 'label.font': 'bold 12px arial, sans-serif'//设置文本的font }); node.setSize(0.6, 0.6);//设置节点大小。由于js中每个点之间的偏移量太小,所以我不得不把节点设置小一些 node.setImage('images/旋转箭头.json');//设置节点的图片 node.a('alarmColor1', 'rgb(150, 150, 150)');//attr属性,可以在这里面设置任何的东西,alarmColor1是在上面设置的image的json中绑定的属性,具体参看 HT for Web 矢量手册(http://www.hightopo.com/guide/guide/core/vector/ht-vector-guide.html#ref_binding) node.a('alarmColor2', 'rgb(150, 150, 150)');//同上 node.a('tpNode', true);//这个属性设置只是为了用来区分“换乘站点”和“小站点”的,后面会用上 } }

问问小秘 2020-01-07 11:16:33 0 浏览量 回答数 0
阿里云大学 云服务器ECS com域名 网站域名whois查询 开发者平台 小程序定制 小程序开发 国内短信套餐包 开发者技术与产品 云数据库 图像识别 开发者问答 阿里云建站 阿里云备案 云市场 万网 阿里云帮助文档 免费套餐 开发者工具 云栖号物联网 小程序开发制作 视频内容分析 企业网站制作 视频集锦 代理记账服务 2020阿里巴巴研发效能峰会 企业建站模板 云效成长地图 高端建站 云栖号弹性计算 阿里云云栖号 云栖号案例 云栖号直播